Kotlin - Почему мы должны явно указывать параметр (ы) типа для универсального метода? - PullRequest
0 голосов
/ 06 декабря 2018

Я работаю над методом расширения следующим образом:

infix fun <T> T.isNullOr(other: T): Boolean {
    if (this == null) return true
    return this == other
}

и я пытаюсь использовать этот метод следующим образом.

val thisShouldWork = true isNullOr true // this is true
val thisShouldNotWork = true isNullOr 0 // No compilation errors?

Я ожидал ошибки компиляции, потому что параметр типа автоматически устанавливается на Boolean для isNullOr, но это не так.Что происходит?

Не понимаю ли я об этом?

в C #, тот же код работает хорошо, как я ожидал.

static bool IsNullOr<T>(this T t, T other) {
    if (t == null) return true;
    return Equals(t, other);
}

bool howAboutThis = 0.IsNullOr(0);
bool andThis = 0.IsNullOr(false); // error - cannot detect type parameter for this

Ответы [ 5 ]

0 голосов
/ 06 декабря 2018

Просто помните, что информация об универсальном типе стирается во время выполнения, и всякий раз, когда вы пытаетесь поместить что-то в метод, который принимает обобщенные элементы, принимается общий знаменатель, например:

listOf("one", 123) // -> assumes T:Any and therefore gives List<Any>

Теперь для вашего примера этобудет означать, что "one".isNullOr(123) оба станут Any.

Однако в качестве идентификатора, если вы объявите определенный тип (например, List<String>), как показано ниже, он не будет работать, чтобы назначить ему другой тип:

val test : List<String> = listOf(123) // this will not work

Уже во время компиляции известно, что данный int не может стать строкой.Однако этот пример не поможет, так как вы не возвращаете этот универсальный тип.Если ваш метод выглядел немного иначе, например, имел бы универсальный тип в качестве возвращаемого значения, он мог бы легко сработать аналогично примеру List.

Так что, чтобы исправить ваш пример, вам нужно указатьтип, который в основном сделает infix устаревшим, например, следующее будет работать так, как вы ожидаете:

val someString : String? = TODO()
val works = someString.isNullOr<String?>("other")
val doesntWork = someString.isNullOr<Int?>(123) // does not nor does:
val doesntWorkToo = someString.isNullOr<String?>(123)

Обратите внимание, что для того, что вы показали, некоторые стандартные функции могут помочь вам (но не устранят эту конкретнуюпроблема), т.е. с использованием ?: (оператор Элвиса) с ?.let:

val someVal : String? = "someString given from somewhere"
val thisWorks = someVal?.let { 
                     it == "some other string to compare" 
                } ?: true /* which basically means it was null */
val thisWillNot = someVal?.let {
                     it == 123 // compile error (funny enough: it.equals(123) would work ;-)
                  } ?: true /* it is null */
0 голосов
/ 06 декабря 2018

Если вы действительно хотите предотвратить это, вы можете:

class IsNullOr<T>(val x: T) {
    operator fun invoke(other: T): Boolean {        
        if (x == null) return true
        return x == other
    }
}

fun <T> T.isNullOr() = IsNullOr(this)

fun main(args: Array<String>) {
    val thisShouldWork = true.isNullOr()(true) // compiles
    val thisShouldNotWork = true.isNullOr()(0) // doesn't compile
}

Это делает вывод типа зависимым только от получателя isNullOr.Если val s может быть общим, вы даже сохраните оригинальный синтаксис (но они не могут).

0 голосов
/ 06 декабря 2018

Я думаю, что в этом случае генерики не имеют большого значения.Вы вызываете только equals в методе, который вы можете сделать для любого типа.По сути, это то же самое, что и

infix fun Any.isNullOr(other: Any): Boolean {
    return this == other
}

Компилируется без проблем, потому что вы всегда можете вызвать equals с чем угодно: other: Any?

0 голосов
/ 06 декабря 2018

Спасибо за ответы.Я думаю, что нет способа предотвратить это на уровне компиляции, поэтому я решил проверить тип для other.

inline infix fun <reified T> T.isNullOr(other: T): Boolean {
    if (this == null) return true
    if (other !is T) return false
    return this == other
}
0 голосов
/ 06 декабря 2018

Здесь val thisShouldNotWork = true isNullOr 0 равно val thisShouldNotWork: Boolean = true.isNullOr<Any>(0).Введите параметр так же, как и ближайший родительский элемент.

И тип возвращаемого значения функции основан на оценке логического выражения : this == other.Давайте посмотрим == объявление функции: public open operator fun equals(other: Any?): Boolean.Он получает Any?.

Параметр типа в этой функции не имеет ничего общего с Boolean.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...