为什么当我编写 MyConsumer 实现 Consumer时,这会显示错误<String><String>?


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遮蔽。

一些声明可能在其作用域中被另一个同名声明遮蔽,在这种情况下,不能使用简单名称来引用声明的实体。

类型nd声明遮蔽了在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,所以这里没有问题。

相关内容

最新更新