2016-10-31 10 views
5

W następującym kodzie "Happy Halloween!", 42 itp. Są oznaczone jako "Niezgodność typów". (Wymagane: T, Found: String (lub Int)), ale czy kompilator nie powinien być w stanie wywnioskować, że zwracana wartość jest poprawnego typu z kontroli typu?Dlaczego pojawia się niezgodność typów podczas próby zwrócenia wartości dla sprawdzonego parametru ogólnego?

interface Type<T> 
class StringType() : Type<String> 
class IntType1() : Type<Int> 
class IntType2(val a: Int, val b: Int) : Type<Int> 

fun <T> something(type: Type<T>): T = when (type) { 
    is StringType -> "Happy Halloween!" 
    is IntType1 -> 42 
    is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b 
    else -> throw IllegalArgumentException() 
} 
+0

Ustawiono typ zwrotu funkcji na 'T', ale zwracasz ciąg' Happy Halloween! '. Właśnie dlatego pojawia się niedopasowanie typu. – marstran

+0

@marstran Typem zwracanym jest 'T', ale po' type jest StringType' return' true' Oczekuję, że kompilator będzie wiedział, że 'T' jest rzeczywiście' String'. – mfulton26

+0

Ah, teraz widzę :) Nie jestem pewien, dlaczego to nie działa tak, jak ty to zrobiłeś, ale działa, jeśli zmienisz linię na 'jest StringType ->" Happy Halloween! " as T' – marstran

Odpowiedz

0

Gdy kompilator stosuje wymazywanie typu, typ powrotu zostanie zdefiniowany. Więc powiedzmy, użyć String ... metoda będzie tak:

fun something(type: Type<String>): String = when (type) { 
    is StringType -> "Happy Halloween!" 
    is IntType1 -> 42 //Wrong: you must return String! 
    is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b 
    else -> throw IllegalArgumentException() 
} 

To jest to: trzeba znać swój typ zwracany w czasie kompilacji. Jeśli nie wiesz, to trzeba powiedzieć, że do kompilatora:

fun <T> something(type: Type<T>): Any = when (type) { 
    is StringType -> "blabla" 
    is IntType1 -> 42 
    is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b 
    else -> throw IllegalArgumentException() 
} 

Niestety ten kod to nie to, czego skacze, masz zamiar zrobić odlew po zwracana jest metoda ...

ale można to:

fun <T> something(type: Type<T>): T = when (type) { 
     is StringType -> type.b 
     //is IntType1 -> 42 remove this! 
     is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b 
     else -> throw IllegalArgumentException() 
    } 

zakładając type.a i type.b są parametized jak T. Następnie kod będzie działać dobrze.

+0

Zaktualizowałem mój przykładowy kod, aby wyjaśnić przypadek użycia. Powinienem był bardziej wyraźny, że byłoby wiele podtypów typu "Type " i że funkcja wykonałaby wiele 'jest ' sprawdza, aby ustalić, co zwrócić. – mfulton26

+0

Przepraszam, moje złe. Edytowałem swoją odpowiedź. Mam nadzieję, że pomoże to teraz. –

0

Gdybym uprościć przykład trochę:

interface Type<T> 

fun <T> something(type: Type<T>): T = when (type) { 
    is Type<String> -> "Happy Halloween!" 
    else -> throw IllegalArgumentException() 
} 

kompilator teraz skarży się, że: cannot check for instance of erased type

Tak więc problem jest to, że ze względu na typ skasowaniem nie ma różnicy między Type<String> i Type<Int> w perspektywie czas, więc kompilator na to nie pozwoli.

Można spróbować użyć czegoś podobnego Gson na TypeToken<T> lub Jacksona TypeReference<T> Docs odnieść się do tego blogu wyjaśniając ideę: http://gafter.blogspot.ca/2006/12/super-type-tokens.html

+0

Tak nie jest: autor celowo użył 'StringType' (podklasy' Type '), ponieważ ** ** możliwe jest sprawdzenie is dla' StringType' w czasie wykonywania. – hotkey

+0

Możliwe jest sprawdzenie, ale nadal będzie się wydawało, że 'StringType' jest po prostu' type'. –

+0

Przepraszam, miałem na myśli, że 'StringType' nadal będzie po prostu' Type <> 'bez dodawania informacji o faktycznym typie. Tak więc "kiedy" nie jest w stanie odróżnić ich. –

0

Operator is, podobnie jak instanceof operatora Java, jest wykonywany w wykonywania.

Tak więc przy czasie kompilacji kompilator nie zna rzeczywistego typu, dlatego pojawia się błąd kompilacji.

Oto kolejny prosty przykład:

fun <T>f(t: T): T 
{ 
    if (t is Int) return 3 // compilation error 
    else return t 
} 
0

Można napisać to:

interface Type<T> 
class StringType() : Type<String> 
class IntType1() : Type<Int> 
class IntType2(val a: Int, val b: Int) : Type<Int> 

inline fun <reified T> something(type: Type<T>): T { 
    val result = when(type) { 
     is StringType -> "Happy Halloween" 
     is IntType1 -> 42 
     is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b 
     else -> throw IllegalArgumentException() 
    } 
    return if (result is T) result else throw Exception() 
} 

uruchamiając następujące:

fun main(args: Array<String>) { 
    println(something(StringType())) 
    println(something(IntType1())) 
    println(something(IntType2(2, 3))) 
} 

daje to wyjście:

Happy Halloween 
42 
19 

Dowiedz się więcej o funkcjach wbudowanych i poprawionych parametrach tutaj: Reified type parameters.

Powiązane problemy