2017-08-20 26 views
5

Gram z refleksją i wyszłam z tym problemem. Podczas korzystania związanego odniesienie klasy poprzez składni ::class, dostaję kowariantna typ KClass:Jaki jest cel posiadania powiązanego odwołania do klasy typu kowariantu?

fun <T> foo(entry: T) { 
    with(entry::class) { 
     this // is instance of KClass<out T> 
    } 
} 

Jak mogę nauczyć się od docs, to powróci dokładny typ obiektu, w przypadku gdy jest instancją podtypu z T, stąd modyfikator wariancji. Jednak to uniemożliwia pobieranie właściwości zadeklarowane w klasie T i coraz ich wartości (co jest, co usiłuję zrobić)

fun <T> foo(entry: T) { 
    with(entry::class) { 
     for (prop in memberProperties) { 
      val v = prop.get(entry) //compile error: I can't consume T 
     } 
    } 
} 

Okazało się, że to rozwiązanie jest przy użyciu javaClass.kotlin funkcję przedłużacza na odwołania do obiektu, aby uzyskać zamiast typ niezmienna:

fun <T> foo(entry: T) { 
    with(entry.javaClass.kotlin) { 
     this // is instance of KClass<T> 
    } 
} 

ten sposób uzyskać zarówno dokładny typ w czasie rzeczywistym i możliwość spożywania typ.

Co ciekawe, jeśli mogę użyć supertypem zamiast generycznego, z drugiej metody I nadal uzyskać dostęp do odpowiedniego typu, bez konieczności wariancji:

class Derived: Base() 

fun foo(entry: Base) { 
    with(entry.javaClass.kotlin) { 
     println(this == Derived::class) 
    } 
} 

fun main(args: Array<String>) { 
    val derived = Derived() 
    foo(derived) // prints 'true' 
} 

Jeśli mam to poprawne, ::class jest równy wywoływaniu java getClass, który zwraca typ wariantu z symbolem wieloznacznym, natomiast javaClass jest getClass z rzutowaniem na określony typ. Nadal nie rozumiem, dlaczego miałbym kiedykolwiek potrzebować kowariantnego KClassa, kiedy ogranicza mnie to tylko do produkcji typu, biorąc pod uwagę, że istnieją inne sposoby dostępu do dokładnej klasy w czasie wykonywania i korzystania z niej swobodnie, i zastanawiam się, czy bardziej bezpośredni ::class powinien zwrócić typ niezmienniczy według projektu.

Odpowiedz

3

Powodem kowariancji w powiązanych odniesieniach ::class jest faktyczny typ środowiska wykonawczego, dla którego wyrażane jest wyrażenie, który może różnić się od zadeklarowanego lub wywnioskowanego typu wyrażenia.

Przykład:

open class Base 
class Derived : Base() 

fun someBase(): Base = Derived() 

val kClass = someBase()::class 

Wyrażenie someBase() jest wpisany jako Base, ale przy starcie to Derived obiekt, który robi oceniona.

Wpisanie someBase()::class jako niezmiennika KClass<Base> jest po prostu niepoprawne, w rzeczywistości ostatecznym wynikiem oceny tego wyrażenia jest KClass<Derived>.

W celu rozwiązania tego możliwa niespójność (które mogłyby spowodować łamany typu bezpieczeństwa), wszystkie związane odniesienia klasy są kowariantna: someBase()::class jest KClass<out Base>, co oznacza, że ​​w czasie wykonywania someBase() może być podtyp Base, a zatem może to być klasą token podtypu Base.

Jest to, oczywiście, nie w przypadku odniesień niezwiązanych klasy: jeśli wziąć Base::class, wiesz na pewno, że jest to klasa dowód Base i nie stanowi niektórych jej podtypami, więc jest niezmienna KClass<Base>.

+0

Rozumiem, co mówisz. Ale chodzi mi o to, że jeśli używam someBase(). JavaClass.kotlin, Nadal mam prawidłowy podtyp (pochodne) w czasie wykonywania, ale także nie ma wariancji w czasie kompilacji, ponieważ te funkcje rozszerzające używają generycznych. Wydaje mi się, że składnia :: klasy mogłaby zostać ulepszona tak, aby działała jak javaClass.kotlin, co jest idiomatycznie mniej bezpośrednie. Wydaje mi się, że robią to samo, zwracając klasę kotlin, ale ta ostatnia wykonuje lepszą pracę, a jednocześnie jest bardziej gadatliwa. – devrocca

+0

Jest odwrotnie: '.javaClass' została wprowadzona wcześniej, a brak wariancji w niej był uważany za błędny projekt; '.javaClass' nawet został przestarzały [(w wersji 1.1 RC)] (https://blog.jetbrains.com/kotlin/2017/02/kotlin-1-1-release-candidate-is-here/), ale potem w wersji ostatecznej 1.1 usunięcie zostało usunięte. – hotkey

+0

Interesujące rzeczy. Jeśli jednak muszę wywoływać właściwości pobrane przez odbicie, potrzebuję odbiornika, a typ kowariantowy nie pozwoli mi użyć związanego. Jak zrobiłbyś to w hipotetycznej sprawie, że '.javaClass' nie był dostępny? – devrocca