Как обойти Несоответствие типов Обязательно: Foo <Type>, Найдено: Foo - PullRequest
0 голосов
/ 19 января 2019

Учитывая следующий код Котлина:

class Foo<T>(val t : T?)

fun <T : Any, R : Any> Foo<T?>.transform(transformer : (T) -> R) : Foo<R?> {
  return when (t) {
    null -> Foo(null)
    else -> Foo(transformer(t))
  }
}

fun main(args : Array<String>) {
  val foo = Foo(args.firstOrNull())

  val bar = foo.transform<String, Int> { t -> t.length }

  val baz = bar.transform<Int, IntRange> { t -> t..(t + 1) }
}

Почему я получаю следующую ошибку: Type mismatch. Required: Foo<String?> Found: Foo<String>

Если я удаляю ? из функции расширения, чтобы быть Foo<T>.transform Вместо этого я получаю следующую ошибку: Type mismatch. Required: Foo<Int> Found: Foo<Int?>

Я могу понять вторую ошибку, потому что вы не можете присвоить Int? Int, но первая не имеет никакого смысла, так как вы можете назначитьString до String?

РЕДАКТИРОВАТЬ:

Я изменил class Foo<T> на class Foo<out T>, и это работает для меня, так как значение t будет прочитано только послепервоначальное назначение.С этой опцией мне не нужно определять параметры типа на сайте вызова transform.

Другая найденная мною опция, которая мне кажется немного грязной (и я не уверен, почему это имеет значение)добавив параметр третьего типа в функцию расширения следующим образом:

fun <T : Any, U : T?, R : Any> Foo<U>.transform(transformer : (T) -> R) : Foo<R?>

С другой стороны, сайт вызова этого приложения я считаю немного странным.Глядя на приведенный выше код, вызов foo.transform НЕ ДОЛЖЕН включать параметры типа, но вызов bar.transform<Int, Int?, IntRange> MUST включает параметры типа для работы.

Этот параметр позволяет установить значение t на более позднем этапе, если оно будет var вместо val.Но он также удаляет умное приведение к t в функции transform.Хотя это можно обойти с помощью !!, если вас не волнуют условия гонки или (с некоторыми дополнительными усилиями) ?: или ?., если вас беспокоит состояние гонки.

Ответы [ 2 ]

0 голосов
/ 19 января 2019

Поскольку вы указываете свойство t в конструкторе как T?, вам не нужно указывать Foo<T?> в качестве получателя и Foo<R?> в качестве возвращаемого типа.Вместо этого используйте Foo<T> и Foo<R>, и это будет работать.

class Foo<T>(val t : T?)

fun <T: Any, R: Any> Foo<T>.transform(transformer : (T) -> R) : Foo<R> {
    return when (t) {
        null -> Foo(null)
        else -> Foo(transformer(t))
    }
}

fun main(args : Array<String>) {
    val foo = Foo(args.firstOrNull())
    val bar = foo.transform { t -> t.length }
    val baz = bar.transform { t -> t..(t + 1) }
}

Примечание: Вам не нужно указывать универсальные типы для преобразования, потому что они могут быть выведены (вхотя бы в этом примере).

0 голосов
/ 19 января 2019

Вы можете изменить свой Foo<T> класс на , а не инвариант (см. https://kotlinlang.org/docs/reference/generics.html):

class Foo<out T>(val t : T?)

fun <T : Any, R : Any> Foo<T?>.transform(transformer : (T) -> R) : Foo<R?> {
  return when (t) {
    null -> Foo(null)
    else -> Foo(transformer(t))
  }
}

fun main(args : Array<String>) {
  val foo = Foo(args.firstOrNull())

  val bar = foo.transform<String, Int> { t -> t.length }

  val baz = bar.transform<Int, IntRange> { t -> t..(t + 1) }
}

out T определяет именно то поведение, которое вам нужно.

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