【问题标题】:Why is one class valid while the other is not?为什么一类有效而另一类无效?
【发布时间】:2010-06-23 20:16:10
【问题描述】:

如您所见,具有非 void 返回类型很重要。

class TestValid {

public String f(List<String> list) {
    return null;
}

public Integer f(List<Integer> list) {
    return null;
}

public void test() {
    f(Arrays.asList("asdf"));
    f(Arrays.asList(123));
}

}

class TestInvalid {

public void f(List<String> list) {
    System.out.println("strings");
}

public void f(List<Integer> list) {
    System.out.println("numbers");
}

}

【问题讨论】:

标签: java eclipse generics overloading


【解决方案1】:

TestValid 开头无效:

TestValid.java:9: name clash: f(List<Integer>) and f(List<String>)
have the same erasure
public Integer f(List<Integer> list) {
               ^

所以返回类型是红鲱鱼。如果在确定重载签名时考虑了返回类型,那么它是有效的,但目前它就像这样无效:

// Equally invalid
public Integer f() {}
public String f() {}

如果你真的设法让TestValid 编译,我很想知道你使用的是哪个编译器。

【讨论】:

  • 有趣的事实:如果您确实设法创建了TestValid(使用非标准编译器、ASM 字节码工程或其他),标准 Java 编译器将链接到基于泛型类型参数。
  • @erickson:泛型参数不是在字节码级别擦除了吗?你能详细说明一下吗?
  • @aioobe - 类型擦除后方法的签名是相同的,这就是为什么股票编译器正确地拒绝它们的原因。但是,有关于类中留下的类型参数的信息。您可以通过反射找到这一点,编译器使用它来告诉您不能将List&lt;Integer&gt; 传递给采用List&lt;String&gt; 的(已编译和擦除)方法。然而,在我描述的 hack 中,我认为编译器实际上是在查看返回类型。
  • 是的,基本理论说它不应该起作用。它可以用 eclipse 3.5 编译并运行良好,但不能用 eclipse 3.6 编译。
  • 返回类型不是红鲱鱼 - 这是编译器(错误地)接受它的原因。虽然返回类型不用于重载解决方案,但由于具有不同的预擦除类型,这些方法不是重载等效的。
【解决方案2】:

在第二种情况下,方法f由于类型擦除而在运行时无法区分。

http://java.sun.com/docs/books/tutorial/java/generics/erasure.html

因此它们都具有完全相同的签名。

【讨论】:

    【解决方案3】:

    经过编译的类型擦除部分,List&lt;String&gt;List&lt;Integer&gt;都是真正的List类型;在 TestInvalid 中,您正在创建两个具有相同运行时签名的方法。

    【讨论】:

      【解决方案4】:

      不支持 JDK1.5 之前的协变返回类型;想想 Object.clone() 方法。以下内容可能会引起您的兴趣:

      public class Base {
          public String f_array(List<String> strings) {
              StackTraceElement current = Thread.currentThread().getStackTrace()[1];
              System.out.println(String.format("%s#%s(strings)", current.getClassName(), current.getMethodName()));
              return null;
          }
      
          public Integer f_array(List<Integer> ints) {
              StackTraceElement current = Thread.currentThread().getStackTrace()[1]; 
              System.out.println(String.format("%s#%s(ints)", current.getClassName(), current.getMethodName()));
              return null;
          }
      
          public Number f() {
              StackTraceElement current = Thread.currentThread().getStackTrace()[1]; 
              System.out.println(String.format("%s#%s()", current.getClassName(), current.getMethodName()));
              return null;
          };
      
          public static class Child extends Base {
              @Override
              public Integer f() { //note Integer is_a Number
                  StackTraceElement current = Thread.currentThread().getStackTrace()[1]; 
                  System.out.println(String.format("%s#%s()", current.getClassName(), current.getMethodName()));
                  return null;
              }
          }
      
          public static void main(String... args) {
              Base c = new Base();
              c.f_array(Arrays.asList(1));
              c.f_array(Arrays.asList("1"));
              c.f();
              c = new Child();
              c.f_array(Arrays.asList(1));
              c.f_array(Arrays.asList("1"));
              c.f();
          }
      }
      

      【讨论】:

        【解决方案5】:

        对于 TestValid 类: 功能似乎超载。如果调用者传递了一个类型为定义的参数 List 对象,则不会出现编译时错误。例如:new ArrayList()new ArrayList()。 因为类型擦除后的签名(返回类型和输入参数)不一样。 但是,如果您传入 new ArrayList(),则会出现编译时错误。

        第二个定义违反了重载函数的基本原则,其中两个函数在类型擦除后具有相同的签名(返回类型和输入参数)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-03
          • 2023-03-21
          • 1970-01-01
          • 1970-01-01
          • 2023-03-17
          • 2014-02-04
          相关资源
          最近更新 更多