为什么我不能在接收参数化参数的方法中使用通配符?

时间:2010-01-27 22:25:06

标签: java generics wildcard parameterized

例如,我使用方法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 ?(或任何其他数字)意味着什么,以及是否有一种优雅的方式来摆脱这种情况,我将不胜感激。到目前为止,我删除了<?>并设置了调用方法@SuppressWarnings("unchecked")(因此我传递了未经检查的Unit而不是Unit<?>),一切都按预期工作,但我'我只是好奇这个,我觉得压制警告不是一个好习惯(不是有点像空挡块吗?)。

谢谢!

编辑:添加一些代码。

(对不起,这很长,但它详细解释了我所坚持的内容。)

我正在使用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 km”。但是我收到警告“ Measure是一种原始类型。在第一次Measure出现时引用通用类型Measure&lt; Q&gt;参数化”并警告“类型安全:方法doubleValue(Unit)属于原始类型Measurable。对泛型类型的参考可测量&lt; Q&gt;应参数化“over measure.doubleValue(Unit.valueOf("km"))

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

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

然后我在分配的右侧部分收到错误消息“类型不匹配:无法从Measure&lt; capture#1-of?&gt;转换为Measure&lt; Length&gt; ”。它(Eclipse)让我把正确的部分投射到(Measure&lt;Length>)。但后来我在右边的部分得到了警告信息“类型安全:从测量&lt;捕获#1-of?&gt;中未经检查的强制转换为测量&lt;长度&gt; ”。修正建议:@SuppressWarnings我更愿意避免(因为我想是偏执的原因)。

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

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

此线上没有任何警告或错误;太棒了。但是,在第二行:

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

我收到了doubleValue的错误消息:“类型为Measurable&lt; capture#3-of?&gt;的方法doubleValue(Unit&lt; capture#3-of?&gt;)不适用对于参数(单位&lt; capture#4-of?&gt;)“。它建议将Unit.valueOf("km")转换为(Unit<?>)。精细。现在我在完全相同的位置收到错误消息:“类型Measurable&lt; capture#3-of?&gt;中的方法doubleValue(Unit&lt; capture#3-of?&gt;)不适用于参数(单位&lt;捕获#5-of?&gt;)“。请注意,数字已经改变,因此不是完全相同的参数,而是类似的原因。然后它执行完全相同的建议,这导致代码中没有任何变化,因为它已经完成。

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

4 个答案:

答案 0 :(得分:3)

度量类看起来像这样吗?

class Measure<T> {

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

}

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

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


<强>更新

valueOf(CharSequence)会返回未知类型的Measure - Measure<?>。要将此安全地转换为您期望的Measure类型,您必须使用Measure.asType()方法。与目标Unit一样,由Unit.valueOf(CharSequence)方法创建。

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类文档中的示例。它们将提供一些额外的深度。

答案 1 :(得分:2)

Java泛型中的通配符表示“未知类型”。在这里,您将doubleValue()方法定义为接受Unit<something1>,其中方法未指定something1。然后,您将调用者知道的值Unit<something2>传递给某个未知something2。编译器错误消息表示没有任何内容可以保证something1something2指定相同的内容。

试试这个:

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

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

答案 2 :(得分:1)

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

这一点很有意思

  

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

你是偏执狂是正确的,但要想一想:你告诉Measure班解析任意String,并返回任何类型的Measure。显然,在这种情况下,您可能会或可能不会得到Measure<Length>(您可能会传递“3公斤”),因此留给图书馆的唯一选择是返回Measure<?>。如果你想要一个Measure<Length>,那么你必须以某种方式强制它 - 这在编译时不可能保证安全。

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

但是甚至更好(万岁!)JSR-275确实为您提供了解决方法,将错误处理推送到“正确”的位置(在获取{{1}时) },在使用它之后的某个时刻。尝试

Measure

Measure<Length> = Measure.valueOf("3 m").asType(Length.class); 返回asType的相应通用版本,如果解析单元的维度不是长度,则会失败并出现异常 - 这几乎可以肯定你想要的,对吗?

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

答案 3 :(得分:0)

我无法重现你的问题,即以下编译好(至少使用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>());
}