简体   繁体   English

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

[英]How to pass bounded wildcard type argument in Kotlin?

The class used (in Java, third party API, not changeable): 使用的类(在Java中,第三方API,不可更改):

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

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

And now I want to call this from Kotlin: 现在我想从Kotlin那里打电话:

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

bookmark.page is in Java, and it is: public Class<? extends WebPage> getPage() bookmark.page是Java,它是: public Class<? extends WebPage> getPage() public Class<? extends WebPage> getPage()

None of these work: 这些都不起作用:

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

Error: Not enough information to infer parameter T in constructor Bookmarkable PageLink<T : Any!, C : Page!>(...) 错误:没有足够的信息来推断constructor Bookmarkable PageLink<T : Any!, C : 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))

This would be the "hypothetically correct" way to do this in Javaish-speak (just the intention, but it's not real code), but this isn't supported by Kotlin : 这将是用Javaish发言的“假设正确”方式(只是意图,但它不是真正的代码),但Kotlin不支持这一点

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

My best workaround is this, which is ugly, but works: 我最好的解决方法就是这个,这很难看,但有效:

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

Surprisingly in Java this was simply: 令人惊讶的是,这简直就是:

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

So far as I understand, BookmarkablePageLink(...) should be approximately equivalent to new BookmarkablePageLink<> in Java, so this is the option which "should" work. 据我所知, BookmarkablePageLink(...)应该与Java中的new BookmarkablePageLink<>大致相同,因此这是“应该”工作的选项。 All others you tried shouldn't, each for different reasons. 你试过的所有其他人都不应该出于不同的原因。

Constructors which have their own type parameters are very rare (before seeing this question I thought they were illegal), so they may be overlooked somewhere in Kotlin compiler. 具有自己的类型参数的构造函数非常罕见(在看到这个问题之前我认为它们是非法的),因此在Kotlin编译器中可能会忽略它们。 A possible workaround is to make it a function instead: 一种可能的解决方法是使其成为一种功能:

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

and then 接着

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

I'll also note that I'm pretty sure 我还要注意,我很确定

the "correct" way to do this in Java-speak 在Java中说“正确”的方法

is actually wrong; 实际上是错的; and in fact you can't write down the type parameters in Java explicitly, because the second type parameter is a captured wildcard . 实际上,您无法明确地在Java中记下类型参数,因为第二个类型参数是捕获的通配符

I am creating a answer from all the best comments because those already seem very valuable. 我正在从所有最好的评论中找到答案,因为那些看起来非常有价值。

Workaround from the question is already a good start: 问题的解决方法已经是一个良好的开端:

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

Also fair is @AlexeyRomanov's intermediate variable (or a similar intermediate function): 同样公平的是@AlexeyRomanov的中间变量(或类似的中间函数):

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

Also valuable for all who find this question via Google might be a short summary of Kotlin vs Java handling of type-variance as explained in Kotlin's documentation : 对于通过Google发现此问题的所有人来说,也很有价值,可能是Kotlin与Java处理类型差异的简短摘要,如Kotlin的文档中所述

  • in Java the handling is at call-site using wildcards (which you can't use, because call-site is in Kotlin) 在Java中,使用通配符处理呼叫站点(您无法使用,因为呼叫站点位于Kotlin中)
  • and in Kotlin the handling is at declaration site using in and out keywords (which you can't use, because your declaration is in Java) 在Kotlin中,处理是在声明站点使用inout关键字(您不能使用,因为您的声明是Java)

Additionally, Java constructors at call-site only allow to specify type arguments from the class, while in Kotlin the constructor call has two type arguments: one from the class and the other from the constructor. 另外,call-site的Java构造函数只允许从类中指定类型参数,而在Kotlin中,构造函数调用有两个类型参数:一个来自类,另一个来自构造函数。 So in Java, we have to say 所以在Java中,我们不得不说

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

and in Kotlin 在Kotlin

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

despite both calling the same constructor with the same arguments. 尽管两个都使用相同的参数调用相同的构造函数。

Given that Kotlin chose an approach for variant types which is the exact opposite of Java's, I am still happy, that we only need workarounds in so few cases. 考虑到Kotlin为变体类型选择了一种与Java完全相反的方法,我仍然很高兴,我们只需要在极少数情况下需要解决方法。 ;-) ;-)

请试试

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM