-
Notifications
You must be signed in to change notification settings - Fork 368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Default values for properties in interfaces #102
base: master
Are you sure you want to change the base?
Default values for properties in interfaces #102
Conversation
What's the problem with declaring the getter explicitly?
|
Such approach completely changes semantics of property, property with field now behaves as property with getter. |
@udalov Because it is not mutable. It is important in cases when we need to use properties with underlying fields. |
@MarcinMoskala Interfaces cannot have states (and mutable field in an interface is a state), so I suppose this is just impossible. You can use some global state to save default value. |
Interfaces cannot have methods either. But if you compile and decompile to Java this file: interface I {
fun a(): String {
return "Bla bla bla"
}
} You will find: public interface I {
@NotNull
String a();
@Metadata(
mv = {1, 1, 9},
bv = {1, 0, 2},
k = 3
)
public static final class DefaultImpls {
@NotNull
public static String a(I $this) {
return "Bla bla bla";
}
}
} There is static function for every method with default body. When you use this interface: class J: I Under the hood, method is implemented and filled with default body: public final class J implements I {
@NotNull
public String a() {
return I.DefaultImpls.a(this);
}
} The same mechanism can be easily applied for properties. Property is getter or getter and setter. Property in interface needs to be overridden. Although it has a default value, then the class that implements it should make a property with the default value. It should be intuitive that this: interface I {
var a = 10
}
class A: I
class B: I
class C: I Should work the same as this: interface I {
var a: Int
}
class A: I {
override var a = 10
}
class B: I {
override var a = 10
}
class C: I {
override var a = 10
} To find use cases, just look at any "Base" classes. They are sometimes huge and they have a lot of different responsibilities. We would strongly prefer having smaller classes with separate responsibilities. |
Let's replace
Now, there are many questions:
or this?
|
Thank you @udalov. These are very good questions. This is how I see it: interface A {
val a = 1
}
class B: A {
val b = a
} Should give the same result as this: interface A {
val a: Int
}
class B: A {
val a: Int = 1
val b = a
} ad 2. This scope should be similar as in classes. interface I {
fun f() = 1
}
class A: I {
}
class B: I {
override fun f() = 3
}
fun main(args: Array<String>) {
print(B().f()) // 3
} and also the same as in Interface Delegation: Default initializer should be used only if the property is not initialized yet. ad 1. Default initialization is just a function, and this is how it should be held. So this: interface I {
var a = computeA()
} Should be compiled to something like this: interface I {
public A getA()
public void setA(A a)
static A defailt$A() {
return computeA()
}
} And in class, if the property is not initialized then there is generated initialization that fills value using In general this interfaces can act like restricted classes or, if you prefer, as interface and delegate. So this example: interface I {
var a = computeA()
}
class B: I Can be similar as: class DefaultI: I {
override var a = computeA()
}
interface I {
var a: A
}
class B: I by DefaultI() But much simpler to use. |
I think there are some use-cases for small class hierarchies where this works great. But still I have some problem understanding what happens if for example an interface wants to count the number of instances of itself like this:
I think it would be more reasonable to not extend interfaces like this but to provide real traits. |
What I propose is a default initialization method that it is used only if no other method is defined. In this case we don't have any conflicting implementations so we know how this property should be initialized. Generated
So the output should be 1 and 2. The same as for methods:
When we have conflicting properties with different default initialization methods, we should be forced to override such member. This is the same problem we have with default bodies. |
@MarcinMoskala can you provide a useful usecase which cannot be implemented using current interface declaration rules? |
Just as we can have default bodies for methods in interfaces, we should be able to give default values for properties:
Under the hood it can work just as default bodies for methods - the compiler can use default property if the property is not overridden.