Take a look at the following code:
class A {
var a = 0
class B {
var b: Int { a }
}
}
It seems reasonable, but compiler thinks the other way: error: instance member 'a' of type 'A' cannot be used on instance of nested type 'A.B'
This assumption would be correct if this code had been used like this: A.B(), since it depends on value that doesn't exist.
But isn't use like this - A().B() - perfectly sound?
Don't you think that this behaviour should be changed?
Tino
(Tino)
2
Unless I'm getting something completely wrong: Absolutely not.
There is no connection between an instance of the inner type to the outer type, and A() is not a namespace, but a plain object.
What is your use case for this wish?
1 Like
AlexanderM
(Alexander Momchilov)
4
I can talk about this from the Java perspective, which supports inner classes.
Their inner classes default to non-static, that is, the inner class can't be talked about separate from the instance it belong to. Each instance of the non-static inner class has an implicit reference to the parent instance.
Java Example
class Outer {
int i = 123;
void printInfo() {
System.out.println("Outer.printInfo()");
System.out.println("\tthis: " + this);
System.out.println("\ti: " + i);
}
class Inner {
void printInfo() {
System.out.println("\nInner.printInfo()");
System.out.println("\tthis: " + this);
System.out.println("\tParent: " + Outer.this); // Has access to the parent object
System.out.println("\tParent's i: " + i); // Has implicit access to parent object fields/methods
}
}
static class StaticInner {
void printInfo() {
System.out.println("\nStaticInner.printInfo()");
System.out.println("\tthis: " + this);
// System.out.println("\tParent: " + Outer.this); // No access to the parent object
// System.out.println("\tParent's i: " + i); // No access to parent object fields/methods
}
}
}
class Untitled {
public static void main(String[] args) {
var outer = new Outer();
outer.printInfo();
var inner = outer.new Inner(); // Notice how this construction is tied to `outer`
inner.printInfo();
var staticInner = new Outer.StaticInner(); // Notce how this construction stands alone
staticInner.printInfo();
}
}
Output:
Outer.printInfo()
this: Outer@6d1e7682
i: 123
Inner.printInfo()
this: Outer$Inner@68fb2c38
Parent: Outer@6d1e7682
Parent's i: 123
StaticInner.printInfo()
this: Outer$StaticInner@2eafffde|
I think there's few things to note:
-
Swift needs to be weary of reference cycles. An implicit reference to the parent instance's variable (like the one you wrote) means there's a strong reference. If it were weak, a would be optional.
This means that parent -> child references would have to be weak, which is backwards to the usual Swift convention
-
I think there's a good argument to be made that "non-static by default" was the wrong choice. Inner classes should have been static by default, and opt-in to being instance-bound, if such a feature exists, at all.
-
Non-static inner classes are usually suspicious. They let you break down your large classes, but only on a technicality. The resulting class cluster is heavily coupled (by all the implicit references to parent fields/methods), in a way that doesn't make them very useful as a method of decomposition. I have found it useful as an intermediate refactoring step, on the trip to standalone classes with proper interfaces between each other.
-
In Java, they even have access to the parent instance's private state. In a sense, this makes them very similar to Swift's extension feature (when both the extension and target type are in the same module), or like a limited friend in C++.
-
If you want your outer and inner class to have this kind of relationship, you can just pass the parent as an argument to the child's initializer. Sure, it's a bit of extra code, but I think that the syntactic salt is worth it, in order to discourage people from building large, coupled balls of mud.
3 Likes
Ray_Fix
(Ray Fix)
5
Perhaps you meant:
class A {
static var a = 0
class B {
var b: Int { a }
}
}
Which does work. This goes to the idea that A just acts as a namespace. If you want b to vary on the different instances of A, @wtedst 's solution is good.