【问题标题】:Why can't I use wildcard with methods receiving a parameterized argument?为什么我不能在接收参数化参数的方法中使用通配符?
【发布时间】:2010-01-27 22:25:05
【问题描述】:

例如,我使用Measure.doubleValue(Unit<?> unit) 方法返回测量值的double,以指定的Unit 表示。如果我将 Unit<?> 变量传递给它,我会收到仍然非常神秘(对我而言)的错误消息:

The method doubleValue(Unit<capture#27-of ?>) in the type Measurable<capture#27-of ?> is not applicable for the arguments (Unit<capture#28-of ?>)

如果有人能解释#27-of ?(或任何其他数字)的含义,以及是否有一种优雅的方法可以摆脱这种情况,我将不胜感激。到目前为止,我删除了&lt;?&gt; 并设置了调用方法@SuppressWarnings("unchecked")(所以我传递了一个未经检查的Unit 而不是Unit&lt;?&gt;),一切都按预期工作,但我只是对此感到好奇,我感觉抑制警告不是一个好习惯(是不是有点像空的 catch 块?)。

谢谢!

编辑:添加一些代码。

(对不起,这很长,但它详细解释了我卡在什么地方。)

我正在使用JSR-275 version 0.9.4(最近的)。

所以...如果我写这个(非常愚蠢的例子):

Measure measure = Measure.valueOf("3 m");
measure = Measure.valueOf(measure.doubleValue(Unit.valueOf("km")), Unit.valueOf("km"));
System.out.println(measure);

它可以工作并打印“0.0030 公里”。但我收到警告“Measure 是一个原始类型。对泛型类型 Measure 的引用应该参数化”在第一次出现 Measure 和警告“类型安全:方法 doubleValue( Unit) 属于原始类型 Measurable。对泛型类型 Measurable 的引用应在 measure.doubleValue(Unit.valueOf("km")) 上进行参数化"。

看到这些警告,我想我可以这样调整(仅限第一行):

Measure<Length> measure = Measure.valueOf("3 m");

然后我在分配的右侧收到错误消息“类型不匹配:无法从 Measure 转换为 Measure”。它(Eclipse)让我可以将正确的部分投射到(Measure&amp;lt;Length&gt;)。但随后我在右侧部分收到警告消息“Type safety: Unchecked cast from Measure to Measure”。修复建议:@SuppressWarnings 我宁愿避免(我猜是出于偏执的原因)。

所以,我退回到Measure measure = Measure.valueOf("3 m"); 并尝试给Measure 提供通配符,显然,此时它不知道“3 m”是什么意思。它可以是Length,也可以是MassTime。所以我得到:

Measure<?> measure = Measure.valueOf("3 m");

并且这一行没有警告或错误;极好的。但是,在第二行:

measure = Measure.valueOf(measure.doubleValue(Unit.valueOf("km")), Unit.valueOf("km"));

我收到doubleValue 的错误消息:“Measurable 类型中的方法 doubleValue(Unit) 不适用于参数 (单位)”。它建议将Unit.valueOf("km") 转换为(Unit&lt;?&gt;)。美好的。现在我在完全相同的位置收到错误消息:“Measurable 类型的方法 doubleValue(Unit) 不适用于参数 (Unit)"。请注意,数字已更改,因此这不是完全相同的参数,而是类似的原因。然后它会执行完全相同的建议,这不会导致代码发生任何变化,因为它已经完成了。

这就是困扰我的地方。让它工作的唯一方法似乎是@SuppressWarnings 或忽略它们。是不是很奇怪?

【问题讨论】:

  • 您有一些我们可以查看的实际代码吗?泛型是非常“魔鬼在细节中”的东西。
  • 我同意克里斯的观点。您可以通过使用Unit&lt;Object&gt; 而不是Unit&lt;?&gt; 来解决您的问题,但是没有代码就很难说。
  • 抱歉耽搁了,不能做得更快。我写了一个示例代码,断章取义,因为我认为与现实生活中的代码混合的一百行是不相关的。
  • 额外的代码有帮助。你绝对不应该压制警告; JSR-275 竭尽全力利用泛型。我用解决方案更新了我的答案。

标签: java generics wildcard parameterized


【解决方案1】:

度量类看起来像这样吗?

class Measure<T> {

  double doubleValue(Unit<T> unit) {
    ...
  }

}

在这种情况下,具有Measure&lt;?&gt; 的引用类型并不意味着您可以将任何类型的Unit 传递给doubleValue 方法。相反,这意味着Measure 实例具有未知的泛型类型,并且将Unit 传递给它的doubleValue 方法是不安全的,因为编译器无法确保类型兼容。

通配符表示“任何类型”;它的意思是“未知类型”。


更新:

valueOf(CharSequence) 返回未知类型的 Measure——Measure&lt;?&gt;。要将其安全地转换为您期望的Measure 类型,您必须使用Measure.asType() 方法。与Unit.valueOf(CharSequence) 方法创建的目标Unit 类似。

Measure<?> unknownMeasure = valueOf("3 m");
Unit<?> unknownUnit = Unit.valueOf("km");
Measure<Length> length = unknownMeasure.asType(Length.class);
Unit<Length> kilometer = unknownUnit.asType(Length.class);
length.doubleValue(kilometer);

查看Measure 类文档中的示例。它们将提供一些额外的深度。

【讨论】:

  • 我不是 100% 确定这些类是如何真正定义的。这个库的一个问题是我们有最新的代码,但是我们找不到相同版本的 API,也找不到相同的版本代码。好吧,对于代码来说是的,但由于某种原因它与字节码类不完全相同......(某些功能和方法签名不一样。)
【解决方案2】:

Java 泛型中的通配符表示“未知类型”。在这里,您将doubleValue() 方法定义为接受Unit&lt;something1&gt;,其中something1 未由该方法指定。然后,您为一些未知的something2 传递一个调用者知道为Unit&lt;something2&gt; 的值。编译器错误消息意味着没有任何东西可以保证something1something2 指定相同的东西。

试试这个:

<T> double doubleValue(Unit<T> unit)
{
    ...
}

这意味着doubleValue() 不关心 T 是什么。

【讨论】:

    【解决方案3】:

    扩展其他(优秀)答案,但更具体一点 JSR-275(我目前正在将它用于一个项目)。

    这点很有趣

    修复建议:@SuppressWarnings,我宁愿避免(我猜是出于偏执的原因)。

    您的偏执是对的,但请考虑一下:您是在告诉Measure 类解析任意String,并返回任何类型的Measure。显然,在这种情况下,您可能会或可能不会获得Measure&lt;Length&gt;(您可能会传递“3 kg”),所以图书馆剩下的唯一选择是返回Measure&lt;?&gt;。如果你想要一个Measure&lt;Length&gt;,那么你必须以某种方式强制它为一个——这不可能在编译时保证安全。

    在这种情况下,我认为@SuppressWarnings 是完全可以接受的,假设您知道字符串始终是有效长度。如果没有,您会将不可避免的ClassCastException 推迟到以后,这非常糟糕。

    但是更好(万岁!)JSR-275 确实为您提供了解决此问题的方法,它将错误处理推到“正确”的位置(在获得 Measure 时,不是在之后的某个时候使用它)。试试

    Measure<Length> = Measure.valueOf("3 m").asType(Length.class);
    

    asType 返回Measure 的适当通用版本,或者如果解析单元的维度不是长度,则失败并抛出异常——这几乎肯定是您想要的,对吧?

    我认为这可以解决您的问题。

    【讨论】:

    • 你说得对,.asType() 会解决这个问题。确实很好的答案。但是,就我而言,我仍然有一个问题,因为我对 JSR-275 库的扩展支持不同数量之间的转换。例如,在光域中,我们直接在 nm、THz、Ky 等之间进行转换,所以我不能强制一种类型来测量……但那是另一回事了。谢谢!
    【解决方案4】:

    我无法重现您的问题,即以下编译正常(至少使用 eclipse):

    static double doubleValue(Unit<?> unit) {
        return 0;
    }
    
    static void bla(Unit<?> u) {
        doubleValue(u);
    }
    
    public static void main(String[] args) {
        doubleValue(new Unit<String>());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-03
      • 2010-11-28
      • 1970-01-01
      相关资源
      最近更新 更多