Why does this show error when I write MyConsumer<String> implements Consumer<String>?

huangapple go评论61阅读模式
英文:

Why does this show error when I write MyConsumer<String> implements Consumer<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: 错误: 无法找到符号
System.out.println(s.toUpperCase());
^
符号: 方法 toUpperCase()
位置: 变量 s 的类型为 String
其中 String 是一个类型变量:
String extends Object 声明在类 MyConsumer

这不会出错:

class MyConsumer implements Consumer<String> {
    @Override
    public void accept(String s) {
        System.out.println("hello".toUpperCase());
        System.println(s.toUpperCase());
    }
}

为什么会出错,有人可以解释吗?

英文:
class MyConsumer&lt;String&gt; implements Consumer&lt;String&gt;{
    @Override
    public void accept(String s){
        System.out.println(&quot;hello&quot;.toUpperCase());
        System.out.println(s.toUpperCase());
    }
}

gives error:

ConsumerTest.java:22: error: cannot find symbol
System.out.println(s.toUpperCase());
^
symbol: method toUpperCase()
location: variable s of type String
where String is a type-variable:
String extends Object declared in class MyConsumer

this doesn't give error:

class MyConsumer implements Consumer&lt;String&gt;{
    @Override
    public void accept(String s){
        System.out.println(&quot;hello&quot;.toUpperCase());
        System.out.println(s.toUpperCase());
    }
}

Why can anyone explain?

答案1

得分: 2

以下是翻译的代码部分:

你正在实现以下所示的Consumer接口:

/**
 * 代表接受单个输入参数并返回无结果的操作。与大多数其他功能接口不同,Consumer预计通过副作用来操作。
 *
 * <p>这是一个<a href="package-summary.html">功能接口</a>
 * 其功能方法是{@link #accept(Object)}。
 *
 * @param <T> 操作的输入类型
 *
 * @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.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()); // 编译错误
    }
}

因此,这变成了一个泛型类,仍然需要定义类型参数。因此,编译器无法匹配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());
    }
}

(这会编译通过)

英文:

You are implementing the Consumer interface shown below:

/**
 * 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.
 *
 * &lt;p&gt;This is a &lt;a href=&quot;package-summary.html&quot;&gt;functional interface&lt;/a&gt;
 * whose functional method is {@link #accept(Object)}.
 *
 * @param &lt;T&gt; the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer&lt;T&gt;

This interface has a type parameter called T. To implement this interface one has to specify what the type parameter stands for. For example you can specify that you want to implement it with a String type parameter:

class MyConsumer implements Consumer&lt;String&gt;

Here the &lt;String&gt; means the type parameter T of Consumer is assumed at compile time to be a String.

Hence the overidden accept method has a String parameter:

@Override
public void accept(String s){
    System.out.println(&quot;hello&quot;.toUpperCase());
    System.out.println(s.toUpperCase());
}

This is all fine but when you do this:

class MyConsumer&lt;String&gt; implements Consumer&lt;String&gt;

You are introducing a new type parameter for the MyConsumer class, confusingly called 'String'. Now Consumer<String> is no longer specifying the String class, but referring to the new type parameter. You get the same situation as this:

class MyConsumer&lt;S&gt; implements Consumer&lt;S&gt;{
    @Override
    public void accept(S s){
        System.out.println(&quot;hello&quot;.toUpperCase());
        System.out.println(s.toUpperCase()); // compilation error
    }
}

So this becomes a generic class, for which the type parameter still needs to be defined. Hence the compiler cannot match s.toUppercase().

You can prove this by fully qualifying the references to the String class:

public class MyConsumer&lt;String&gt; implements Consumer&lt;java.lang.String&gt; {
    @Override
    public void accept(java.lang.String s){
        System.out.println(&quot;hello&quot;.toUpperCase());
        System.out.println(s.toUpperCase());
    }
}

(this compiles)

答案2

得分: 0

你的类声明了自己的类型 &lt;String&gt;,这就是为什么没有它就会出错:

class MyConsumer implements Consumer&lt;String&gt; { ...
英文:

Your class declares its own type &lt;String&gt;, which is why without it there is no error:

class MyConsumer implements Consumer&lt;String&gt; { ...

答案3

得分: 0

这个声明中 String 的第一次出现...

class MyConsumer&lt;String&gt; implements Consumer&lt;String&gt;

...只是 MyConsumer 的类型参数的名称。class MyConsumer&lt;String&gt; 中的 String 不是指 java.lang.String。毕竟,从语法上讲,这与以下情况相同:

class MyConsumer&lt;T&gt; implements Consumer&lt;String&gt;

然后,在 accept 方法中,您使用了 String 作为参数类型。这个 String 也不是指 java.lang.String。它实际上是指您刚刚声明的类型参数名为 Stringjava.lang.String 被类型参数 String 遮蔽

在其范围的某个部分,某些声明可能会被另一个相同名称的声明部分遮蔽,因此简单名称无法用来引用已声明的实体。

类型名称为 n 的声明 d 遮蔽了 d 出现的范围内的所有其他名称为 n 的类型的声明。

如果您希望创建一个消费者,用于消耗 java.lang.String,那么您的类不需要是泛型的。

如果您实际上希望使您的类成为泛型的,并且因某种奇怪的原因要使用 String 作为类型参数名称,您应该在声明 accept 的参数时使用完全限定名称 java.lang.String

@Override
public void accept(java.lang.String s){
    ...
}
英文:

The first occurrence of String in this declaration...

class MyConsumer&lt;String&gt; implements Consumer&lt;String&gt;

...is just the name of a type parameter of MyConsumer. String in class MyConsumer&lt;String&gt; does not refer to java.lang.String. After all, this is syntactically the same as:

class MyConsumer&lt;T&gt; implements Consumer&lt;String&gt;

Then, in the accept method, you used String as the parameter type. This String also does not refer to java.lang.String. It instead refers to the type parameter named String you just declared. java.lang.String is said to be shadowed by the type parameter String.

> Some declarations may be shadowed in part of their scope by another declaration of the same name, in which case a simple name cannot be used to refer to the declared entity.
>
> A declaration d of a type named n shadows the declarations of any other types named n that are in scope at the point where d occurs throughout the scope of d.

Since you want a consumer that consumes a java.lang.String, your class doesn't need to be generic.

If you actually want your class to be generic, and want to use String as the type parameter name for some weird reason, you should use the fully qualified name, java.lang.String, when declaring the parameter of accept.

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

答案4

得分: 0

在第一个示例中,String 只是一个"类型名称",就像 T、K、V 或其他一样。在编译过程中,它变成了 Object,它没有toUpperCase方法。你应该了解类型擦除。在第二个示例中,你告诉编译器类型应该是一个字符串,所以这里没有问题。

英文:

In the first example String is just a "type name", just like T, K, V or so. During the compilation it becomes Object which does not have method toUpperCase. You should read about type erasure. In the second example you are telling the compiler that the type should be a String, so no problems in here.

huangapple
  • 本文由 发表于 2023年3月31日 17:54:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75897143.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定