class MyConsumer<String> implements Consumer<String>{
@Override
public void accept(String s){
System.out.println("hello".toUpperCase());
System.out.println(s.toUpperCase());
}
}
给出错误:
消费者测试.java:22:错误:找不到符号System.out.println(s.toUpperCase());^ symbol:method toUpperCase()location:String类型的变量s,其中String是类型变量:String扩展MyConsumer类中声明的Object
这不会给予错误:
class MyConsumer implements Consumer<String>{
@Override
public void accept(String s){
System.out.println("hello".toUpperCase());
System.out.println(s.toUpperCase());
}
}
为什么有人能解释?
4条答案
按热度按时间ttvkxqim1#
您正在实现如下所示的Consumer接口:
这个接口有一个类型参数T。要实现这个接口,必须指定类型参数代表什么。例如,你可以指定你想用一个String类型参数来实现它:
这里的
<String>
意味着Consumer的类型参数T在编译时被假设为String。因此,overidden accept方法有一个String参数:
这一切都很好,但当你这样做:
你为MyConsumer类引入了一个新的类型参数,被混淆地称为'String'。现在Consumer不再指定String类,而是引用新的类型参数。你会得到与下面相同的情况:
所以这就变成了一个泛型类,它的类型参数仍然需要定义。因此编译器不能匹配s.toUppercase()。
你可以通过完全限定对String类的引用来证明这一点:
(this编译)
nbysray52#
你的类声明了自己的类型
<String>
,这就是为什么没有它就不会有错误:ntjbwcob3#
此声明中第一次出现的
String
......只是
MyConsumer
的类型参数的名称。class MyConsumer<String>
中的String
并不引用java.lang.String
。毕竟,这在语法上与以下相同:然后,在
accept
方法中,您使用String
作为参数类型。此String
* 也 * 不引用java.lang.String
。它引用的是您刚刚声明的名为String
的类型参数。java.lang.String
被类型参数String
隐藏。某些声明可能在其作用域的一部分被同名的另一个声明隐藏,在这种情况下,不能使用简单名称来引用声明的实体。
一个名为
n
的类型的声明d
隐藏了任何其他名为n
的类型的声明,这些类型在整个d
范围内的d
出现的地方都在范围内。由于您需要一个使用
java.lang.String
的消费者,因此您的类不需要是泛型的。如果你真的希望你的类是泛型的,* 并且 * 出于某种奇怪的原因想使用
String
作为类型参数名,那么在声明accept
的参数时,你应该使用完全限定名java.lang.String
。nhhxz33t4#
在第一个例子中,
String
只是一个“类型名”,就像T,K,V之类的。在编译过程中,它变成了Object
,它没有方法toUpperCase
。你应该读一下类型擦除。在第二个例子中,你告诉编译器类型应该是String,所以这里没有问题。