如何在Kotlin中传递有界通配符类型参数?

12

所使用的类(在Java中,第三方API,不可更改):

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

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

现在我想从Kotlin中调用这个函数:

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

bookmark.page是Java中的一个类,它的定义如下:public Class<? extends WebPage> getPage()

以下方法均无效:

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

错误: 在constructor Bookmarkable PageLink<T : Any!, C : Page!>(...)中,没有足够的信息可以推断参数T

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))

这将是在Java中实现的“假设正确”的方法(只是意图,但不是真正的代码),但是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() ));

@Zoe:不起作用 - 未解决的引用:T,然后是意外的类型规范 - Hendy Irawan
嗯...为什么有人会对一个没有有效答案且即使尝试阅读官方文档也不容易找到的问题进行投票?奇怪,但这就是生活.. - Hendy Irawan
1
您的代码似乎不匹配:Java调用有2个类型参数和3个参数,声明有1个和2个。而getParams的类型是什么? - Alexey Romanov
不,我只是好奇模板参数似乎根据代码片段并不必要。item.queue<WebPage>(BookmarkablePageLink("link", bookmark.page)) 应该可以工作。 - EpicPandaForce
需要两个类型参数,一个是 T,另一个是 C。 - Hendy Irawan
显示剩余7条评论
3个回答

6
据我所知,BookmarkablePageLink(...) 应该大致相当于 Java 中的 new BookmarkablePageLink<>,因此这是应该可行的选项。其他你尝试的选项都不应该可行,每个选项都有不同的原因。
拥有自己类型参数的构造函数非常罕见(在看到这个问题之前,我认为它们是不合法的),因此它们可能被 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中做这件事的“正确”方法实际上是错误的。

你说得对,“Java术语中的正确方式”是错误的,因为它不是Java。我已经编辑了问题以澄清这一点。在Java中编写它的方式就像我在问题中所写的那样:“令人惊讶的是,在Java中这很简单”。 - Hendy Irawan
这就是为什么我说“应该”,而不是“是”的原因。 - Alexey Romanov
实际上,它无法推断出T而不是C是有用的信息;item.queue期望的类型是什么? - Alexey Romanov
1
你可以尝试一下这个代码:val link: BookmarkablePageLink<Any> = BookmarkablePageLink("link", bookmark.page); item.queue(link)。这将只指定 T = Any,看起来这正是你想要的。 - Alexey Romanov
1
如果只需要使用一次,“val link” 对我来说似乎还可以(尽管可能仍有更好的选项); 如果需要多次,您可以定义 fun linkForClass(id: String, clazz: Class<out Page>): BookmarkablePageLink<Any> = BookmarkablePageLink(id, clazz)(根据您代码中会变化的内容进行修改)。 - Alexey Romanov
显示剩余9条评论

3
我正在从所有最好的评论中创建答案,因为它们已经非常有价值。
问题的解决方法已经是一个不错的开始:
BookmarkablePageLink<Any, WebPage>("link", bookmark.page as Class<WebPage>)

此外,@AlexeyRomanov的中间变量(或类似的中间函数)也是公平的:

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

此外,对于所有通过Google找到这个问题的人来说,Kotlin文档中关于Kotlin与Java处理类型变异的简短摘要也非常有价值:
  • 在Java中,使用通配符在调用站点处理类型变异(由于调用站点在Kotlin中无法使用,因此您无法使用它)
  • 而在Kotlin中,使用inout关键字在声明站点处理类型变异(因为您的声明在Java中,所以您无法使用它)

此外,在Java中,构造函数只允许在调用站点指定类的类型参数,而在Kotlin中,构造函数调用有两个类型参数:一个来自类,另一个来自构造函数。 因此,在Java中,我们必须说:

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

而在 Kotlin 中

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

尽管使用相同的参数调用了相同的构造函数,但两者仍然存在差异。
考虑到Kotlin选择了与Java完全相反的变体类型方法,我仍然很高兴,我们只需要在极少数情况下使用解决方法。;-)

2
请尝试。
item.queue(BookmarkablePageLink<Any, WebPage>("link", bookmark.page)) 

item.queue() 没有类型参数。这肯定行不通。 - Hendy Irawan
这是我尝试过的一件事。它不起作用,因为给定的“WebPage”类型参数与实际的“? extends WebPage”不匹配。 - Hendy Irawan
1
很奇怪。我猜确实是 item.queue(BookmarkablePageLink<Any, WebPage>("link", bookmark.page as Class<WebPage>))。:( - EpicPandaForce

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接