Как передать ограниченный аргумент типа подстановки в Котлин?

Используемый класс (в Java, сторонний API, не изменяемый):

public class BookmarkablePageLink<T> extends Link<T> {

    public <C extends Page> BookmarkablePageLink(final String id, final Class<C> pageClass)

И теперь я хочу назвать это от Котлина:

item.queue(BookmarkablePageLink("link", bookmark.page))

bookmark.page находится в Java, и это: public Class<? extends WebPage> getPage() public Class<? extends WebPage> getPage()

Ни одна из этих работ:

item.queue(BookmarkablePageLink("link", bookmark.page))

Ошибка: недостаточно информации для вывода параметра T в constructor Bookmarkable PageLink<T: Any!, C: Page!>(...)

item.queue(BookmarkablePageLink<>("link", bookmark.page))

item.queue(BookmarkablePageLink<Any>("link", bookmark.page))

item.queue(BookmarkablePageLink<Any, *>("link", bookmark.page))

item.queue(BookmarkablePageLink<Any, WebPage>("link", bookmark.page))

item.queue(BookmarkablePageLink<Any, in WebPage>("link", bookmark.page))

item.queue(BookmarkablePageLink<Any, out WebPage>("link", bookmark.page))

item.queue(BookmarkablePageLink<Any, T : WebPage>("link", bookmark.page))

Это был бы "гипотетически правильный" способ сделать это в Javaish-talk (просто намерение, но это не настоящий код), но это не поддерживается Kotlin:

item.queue(BookmarkablePageLink<Any, ? extends WebPage>("link", bookmark.page))

Мое лучшее обходное решение - это, что уродливо, но работает:

item.queue(BookmarkablePageLink<Any, WebPage>("link", bookmark.page as Class<WebPage>))

Удивительно на Java это было просто:

item.queue(new BookmarkablePageLink<>("link", bookmark.getPage() ));

Ответ 1

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

Обходной путь от вопроса - это уже хорошее начало:

BookmarkablePageLink<Any, WebPage>("link", bookmark.page as Class<WebPage>)

Также справедливо промежуточная переменная @AlexeyRomanov (или аналогичная промежуточная функция):

val link: BookmarkablePageLink<Any> = BookmarkablePageLink("link", bookmark.page)

Также ценным для всех, кто находит этот вопрос через Google, может быть краткий обзор обработки ошибок Kotlin vs Java с изменением типа, как описано в документации Kotlin:

  • в Java обработка выполняется на сайте call-сайта с использованием подстановочных знаков (которые вы не можете использовать, поскольку сайт-колл находится в Котлине)
  • и в Котлин обращение находится на сайте декларации, используя in и out ключевых слов (которые вы не можете использовать, потому что ваше выражение в Java)

Кроме того, конструкторы Java на узле-вызове позволяют указывать только аргументы типа из класса, тогда как в Kotlin вызов конструктора имеет два аргумента типа: один из класса и другой из конструктора. Поэтому в Java мы должны сказать

new BookmarkablePageLink<T>("something", Page.class)

и в Котлине

BookmarkablePageLink<T, Page>("something", Page::class.java)

несмотря на то, что оба вызывали один и тот же конструктор с теми же аргументами.

Учитывая, что Котлин выбрал подход для типов вариантов, который является полной противоположностью Java, я по-прежнему счастлив, что нам нужны только обходные решения в стольких случаях. ;-)

Ответ 2

Насколько я понимаю, значение BookmarkablePageLink(...) должно быть приблизительно эквивалентно new BookmarkablePageLink<> в Java, поэтому это вариант, который должен "работать". Все остальные, которых вы пытались, не должны, каждый по разным причинам.

Конструкторы, у которых есть свои параметры типа, очень редки (перед тем, как увидеть этот вопрос, я думал, что они незаконны), поэтому их можно упустить где-то в компиляторе Kotlin. Возможное обходное решение заключается в том, чтобы сделать его функцией:

fun <T, C : Page> makeBookmarkablePageLink(id: String, clazz: Class<C>): BookmarkablePageLink<T> = 
    BookmarkablePageLink<T, C>(id, clazz)

а потом

item.queue(makeBookmarkablePageLink("link", bookmark.page))

Я также отмечу, что я уверен

"правильный" способ сделать это в Java-говорить

фактически неверно; и на самом деле вы не можете явно записывать параметры типа в Java, потому что параметр второго типа является захваченным подстановочным знаком.

Ответ 3

Пожалуйста, попробуй

item.queue(BookmarkablePageLink<Any, WebPage>("link", bookmark.page))