class MyConsumer<String> implements Consumer<String>{
@Override
public void accept(String s){
System.out.println("hello".toUpperCase());
System.out.println(s.toUpperCase());
}
}
给了错误:
ConsumerTest.java:22: error: cannot find symbolSystem.out.println (s.toUpperCase ());^toUpperCase()location:字符串类型的变量其中String是类型变量:在MyConsumer类中声明的对象
this doesn't give error:
class MyConsumer implements Consumer<String>{
@Override
public void accept(String s){
System.out.println("hello".toUpperCase());
System.out.println(s.toUpperCase());
}
}
为什么有人能解释?
您正在实现如下所示的消费者接口:
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #accept(Object)}.
*
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T>
这个接口有一个名为t的类型参数。要实现这个接口,必须指定类型参数代表什么。例如,您可以指定要使用String类型参数实现它:
class MyConsumer implements Consumer<String>
这里的<String>
表示在编译时假定Consumer的类型参数T为String。
因此重写的accept方法有一个String参数:
@Override
public void accept(String s){
System.out.println("hello".toUpperCase());
System.out.println(s.toUpperCase());
}
这些都很好,但是当你这样做时:
class MyConsumer<String> implements Consumer<String>
你正在为MyConsumer类引入一个新的类型参数,它叫'String',很容易让人混淆。现在Consumer不再指定String类,而是引用新的类型参数。你会得到与下面相同的情况:
class MyConsumer<S> implements Consumer<S>{
@Override
public void accept(S s){
System.out.println("hello".toUpperCase());
System.out.println(s.toUpperCase()); // compilation error
}
}
因此,这变成了一个泛型类,仍然需要为其定义类型参数。因此编译器无法匹配s.toUppercase()。
可以通过完全限定对String类的引用来证明这一点:
public class MyConsumer<String> implements Consumer<java.lang.String> {
@Override
public void accept(java.lang.String s){
System.out.println("hello".toUpperCase());
System.out.println(s.toUpperCase());
}
}
(编译)
你的类声明了它自己的类型<String>
,这就是为什么没有它就没有错误:
class MyConsumer implements Consumer<String> { ...
String
在此声明中的第一次出现…
class MyConsumer<String> implements Consumer<String>
…是MyConsumer
的一个类型参数的名称。class MyConsumer<String>
中的String
不是指java.lang.String
。毕竟,这在语法上与:
class MyConsumer<T> implements Consumer<String>
然后,在accept
方法中,您使用String
作为参数类型。这个String
也不指向java.lang.String
。相反,它引用了您刚刚声明的名为String
的类型参数。java.lang.String
被类型参数String
遮蔽。
一些声明可能在其作用域中被另一个同名声明遮蔽,在这种情况下,不能使用简单名称来引用声明的实体。
类型
n
的d
声明遮蔽了在d
出现在整个d
作用域中的任何其他类型n
的声明。
因为你想要一个消费java.lang.String
的消费者,你的类不需要是泛型的。
如果你真的希望你的类是泛型的,和由于一些奇怪的原因想要使用String
作为类型参数名,你应该在声明accept
的参数时使用完全限定名java.lang.String
。
@Override
public void accept(java.lang.String s){
...
}
在第一个示例中,String
只是一个"类型名称",就像T、K、V等一样。在编译过程中,它变成了Object
,它没有方法toUpperCase
。你应该读一读关于类型擦除的知识。在第二个例子中,你告诉编译器类型应该是String,所以这里没有问题。