Java泛型方法 - 如何调用特定的方法

3

Java中的通用方法能否根据输入类型调用特定的其他方法?

我尝试了下面的示例,希望能够清晰表达问题。

class X {
  public void pr(Object s) {
      System.out.println("Object");
  }

  public void pr(String s) {
      System.out.println("String");
  }

  public <T> void go(T x) {
      this.pr(x);
  }

  public static void main(String[] args) {
    String v = "hello";
    X x = new X();

    // Both print "Object", while I want "String"
    x.go(v);
    x.<String>go(v);
  }
}

我希望go()调用pr(String)。但是无论我怎么做,都会调用pr(Object)。
上面的示例仅用于演示,实际应用要复杂得多。
提前致谢。

谷歌搜索“类型擦除”。这里有数十个相关问题。编译器将该方法编译为public void go(Object x)。因此,x的声明类型是Object - JB Nizet
4个回答

5

很遗憾,因为类型擦除的缘故,你所描述的情况是不可能实现的。在运行时,T 会被视为 Object,因此这里选择了 pr(Object)

一种可能的解决方案是手动测试传递数据的类型,并在稍微进行类型转换的情况下调用正确的方法,例如:

public <T> void go(T x) {
    if (x instanceof String)
        pr((String)x);
    else
        pr(x);
}

但如果你可以编辑传递给go方法的类的代码,那么更好的解决方案是在每个类中添加/实现pr方法,这样你就可以使用多态性来调用它们:

interface CanPR{
    void pr();
}

class Foo implements CanPR{
    @Override
    public void pr() {
        System.out.println("pr from Foo");
    }
}

class Bar implements CanPR{
    @Override
    public void pr() {
        System.out.println("pr from Bar");
    }
}


class X {

    public <T extends CanPR> void go(T x) {
        x.pr();
    }

    public static void main(String[] args) {
        CanPR a = new Foo();
        CanPR b = new Bar();

        X x = new X();

        x.go(a); //prints: pr from Foo
        x.go(b); //prints: pr from Bar
    }
}

实际上,在这种情况下,您甚至不需要在go中使用通用类型,因此可以将其重写为

    public void go(CanPR x) {
        x.pr();
    }

谢谢你的回答。这解释了一些问题,我也学到了类型擦除的知识。 不幸的是,“instanceof”模式对我来说行不通,因为有太多的类可以使用这个东西。我会考虑其他的解决方案。 - Marcin Zukowski
很遗憾,这是Java语言,在早期版本中向后兼容性非常重要,因此编译的代码需要能够在旧版JVM上运行,而旧版JVM不支持泛型类型<T>,所以类型擦除是必要的。现在它已经成为语言的一部分,没有人想要删除它:/ - Pshemo

3

试试这个:

class X {
      public void pr(Object s) {
          System.out.println("Object");
      }

      public void pr(String s) {
          System.out.println("String");
      }

      public <T> void go(T x) {
          if(x instanceof String){
              this.pr((String)x);
          } else {
              this.pr(x);
          }
      }

      public static void main(String[] args) {
        String v = "hello";
        X x = new X();

        // Both print "Object", while I want "String"
        x.go(v);
        x.go(new Object());
//      x.<String>go(v);
      }
    }

问题是String也是一个对象,所以您需要检查它是否为其他类型,可以使用instanceof来实现。


1
此外,在 go 方法中,我们需要检查 x != null,否则它会抛出 MethodAmbiguityException 异常。 - RP-
正确,代码中没有处理,但肯定是需要的。 - Bruno Franco
@Rp- 在这里如何抛出任何异常?如果x == null,则使用s == null调用pr(Object s)。我甚至没有听说过这个异常。 - Kamil Jarosz
@kamil09875:你说得对,没有这样的异常,但如果重载的参数不在继承层次结构中,它可能会抛出类似于java.lang.Error: Unresolved compilation problem: The method is ambiguous?的异常。我的错误,在这里它不会抛出那个异常,因为String是Object的子类。在空值的情况下,将调用带有参数Object的方法。 - RP-
@Rp- 方法可能会产生歧义的唯一情况是当你显式调用方法(null)时;当你调用带已知类型变量var的方法method(var)时,就不可能产生歧义。我试着理解你的想法,你能给我一个可能出现这种情况的小例子吗? :) - Kamil Jarosz
@kamil0985 好的,考虑这样一种情况,你有两个重载方法,一个是带有 Integer 参数,另一个是带有 String 参数,如 public void m1(Integer i) {//something} public void m1(String s) {//somehting}。现在如果你调用 m1(null),它会抛出异常。这是因为 Integer 和 String 不在同一继承层级中,JRE 无法确定调用哪个方法。 - RP-

3

Java泛型是编译时的辅助工具,Java编译器会为泛型去除类型信息。

但你仍然可以这样做:

if (x instanceof String) {
  this.pr( (String) x );
}

0
很遗憾,您所描述的情况是不可能的,因为在运行时由于类型擦除,T 被视为 Object,所以这里选择了 pr(Object)。
不可能。我认为这就是答案。只需查看此处

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接