2016-04-08 14 views
15

Łatwo jest napisać metod rozszerzenie w Kotlin:pola rozszerzeń w Kotlin

class A { } 
class B { 
    fun A.newFunction() { ... } 
} 

Ale czy istnieje jakiś sposób, aby utworzyć zmienną przedłużacz? Jak:

class B { 
    var A.someCounter: Int = 0 
} 

Odpowiedz

24

Nie - documentation wyjaśnia następująco:

Rozszerzenia rzeczywistości nie modyfikować klasy rozciągają. Definiując rozszerzenie, nie wstawia się nowych członków do klasy, a jedynie sprawia, że ​​nowe funkcje można wywoływać za pomocą notacji kropek na instancjach tej klasy.

i

zauważyć, że od rozszerzenia w rzeczywistości nie wkładać do członków klasy, nie ma skuteczny sposób na rozszerzenie własności mieć pole oporowe. Z tego powodu inicjalizatory nie są dozwolone dla właściwości przedłużających. Ich zachowanie można zdefiniować tylko poprzez jawne dostarczanie modułów pobierających/ustawiających.

Myślenie o funkcjach/właściwościach rozszerzeń jako o cukierku syntaktycznym do wywoływania funkcji statycznej i przekazywanie wartości z nadzieją czyni to jasnym.

11

Można utworzyć właściwość przedłużacz z zastąpionej getter i setter:

var A.someProperty: Int 
    get() = /* return something */ 
    set(value) { /* do something */ } 

Ale nie można utworzyć właściwość przedłużacz z polem podkładu, ponieważ nie można dodać pole do istniejącej klasy.

5

Nie można dodać pola, ale można dodać właściwość, która deleguje do innych właściwości/metod obiektu w celu implementacji jego akcesorów. Na przykład załóżmy, że chcemy dodać obiekt secondsSinceEpoch do klasy java.util.Date można napisać

var Date.secondsSinceEpoch: Long 
    get() = this.time/1000 
    set(value) { 
     this.time = value * 1000 
    } 
7

Jest no way to add extension properties with backing fields do klas, ponieważ rozszerzeń do not actually modify a class.

Można zdefiniować tylko właściwość rozszerzenia z niestandardowym programem pobierającym (i programem ustawiającym dla var) lub delegated property.


Jeśli jednak chcesz zdefiniować właściwość rozszerzenia, która zachowałaby się tak, jakby zawierała pole pomocnicze, przydają się właściwości delegowane. Ideą jest stworzenie delegata właściwość, że przechowywania mapowanie obiekt do wartości:

  • użyciu tożsamości, nie equals()/hashCode(), aby rzeczywiście przechowywania wartości dla każdego obiektu, jak IdentityHashMap robi;

  • nie zapobiegając kluczowych obiektów z czym zbierane śmieci (stosując weak references), jak WeakHashMap nie.

Niestety, nie ma WeakIdentityHashMap w JDK, więc trzeba zaimplementować własną (lub wziąć complete implementation).

Następnie, w oparciu o to odwzorowanie, można utworzyć klasę uczestników spełniającą wymagania property delegates requirements. Oto przykład nie bezpieczny wątku wykonania:

class FieldProperty<R, T : Any>(
    val initializer: (R) -> T = { throw IllegalStateException("Not initialized.") } 
) {  
    private val map = WeakIdentityHashMap<R, T>() 

    operator fun getValue(thisRef: R, property: KProperty<*>): T = 
      map[thisRef] ?: setValue(thisRef, property, initializer(thisRef)) 

    operator fun setValue(thisRef: R, property: KProperty<*>, value: T): T { 
     map[thisRef] = value 
     return value 
    } 
} 

Przykład zastosowania:

var Int.tag: String by FieldProperty { "$it" } 

fun main(args: Array<String>) { 
    val x = 0 
    println(x.tag) // 0 

    val z = 1 
    println(z.tag) // 1 
    x.tag = "my tag" 
    z.tag = x.tag 
    println(z.tag) // my tag 
} 

Gdy zdefiniowana wewnątrz klasy odwzorowanie może być przechowywany oddzielnie dla instancje klasy lub też na współdzielony obiekt uczestnika:

private val bATag = FieldProperty<Int, String> { "$it" } 

class B() { 
    var A.someCounter: Int by FieldProperty { 0 } // independent for each instance of B 
    var A.tag: String by bATag // shared between the instances, but usable only inside B 
} 

Należy również pamiętać, że tożsamość is not guaranteed dla pri Java typy myszy z powodu boksu.

Podejrzewam, że wydajność tego rozwiązania jest znacznie gorsza niż w przypadku zwykłych pól, prawdopodobnie zbliżonych do normalnych Map, ale to wymaga dalszych testów.

Aby uzyskać informacje na temat obsługi atrybutów nullable i implementacji wątków, należy zapoznać się z here.

+0

to bardzo fajny twórczy hack. Możesz dodać plik licencji do github. Dzięki! – MadDeveloper