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

o3imoua4  于 2023-04-04  发布在  Java
关注(0)|答案(4)|浏览(128)
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());
    }
}

为什么有人能解释?

ttvkxqim

ttvkxqim1#

您正在实现如下所示的Consumer接口:

/**
 * 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。
因此,overidden 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());
    }
}

(this编译)

nbysray5

nbysray52#

你的类声明了自己的类型<String>,这就是为什么没有它就不会有错误:

class MyConsumer implements Consumer<String> { ...
ntjbwcob

ntjbwcob3#

此声明中第一次出现的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隐藏了任何其他名为n的类型的声明,这些类型在整个d范围内的d出现的地方都在范围内。
由于您需要一个使用java.lang.String的消费者,因此您的类不需要是泛型的。
如果你真的希望你的类是泛型的,* 并且 * 出于某种奇怪的原因想使用String作为类型参数名,那么在声明accept的参数时,你应该使用完全限定名java.lang.String

@Override
public void accept(java.lang.String s){
    ...
}
nhhxz33t

nhhxz33t4#

在第一个例子中,String只是一个“类型名”,就像T,K,V之类的。在编译过程中,它变成了Object,它没有方法toUpperCase。你应该读一下类型擦除。在第二个例子中,你告诉编译器类型应该是String,所以这里没有问题。

相关问题