无法使用Java Generics选择合适的方法

时间:2014-12-06 03:19:17

标签: java generics

这个程序没有做我想要的。它打印“悲伤”两次,而我希望它打印“快乐”然后“悲伤”。

public class Woof {

    public static class Arf<T> {
        T yap;
        public Arf(T yap) {
            this.yap = yap;
        }

        public String woof() {
            /*
             * Should select which doYapStuff() based on whether T
             * happens to be an Integer, or something else.
             */
            return doYapStuff(yap);
        }

        /* Special case implementation of doYapStuff() where T is Integer */
        public String doYapStuff(Integer x) {
            return "happy";
        }

        /* Default implementation of doYapStuff() where T is something else */
        public String doYapStuff(T x) {
            return "sad";
        }
    }

    public static void main(String[] args) {
        Integer i = 5;
        Arf<Integer> arf1 = new Arf<Integer>(i);
        System.out.println(arf1.woof()); // Should print "happy"

        String s = "foo";
        Arf<String> arf2 = new Arf<String>(s);
        System.out.println(arf2.woof()); // Should print "sad"
    }

}

2 个答案:

答案 0 :(得分:9)

在此博客中对此进行了解释:http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ050

相关引言:

  

这怎么可能发生?我们将一个String类型的参数传递给重载方法,然后调用Object类型的版本。原因是编译器仅为每个泛型类型或方法创建一个字节代码表示,并将泛型类型或方法的所有实例映射到该一个表示。

在我们的示例中,泛型方法被转换为以下表示:

(编辑:我更改了方法以匹配此问题)

public String doYapStuff(Object x) {
            return "sad";
}
  

考虑到这种转换,很明显为什么要调用重载方法的Object版本。将什么类型的对象传递给泛型方法然后传递给重载方法完全无关紧要。我们将始终观察对重载方法的Object版本的调用。

     

更一般地说:重载解析在编译时发生,也就是说,编译器决定必须调用哪个重载版本。当泛型方法转换为其唯一字节代码表示时,编译器会这样做。在该转换类型期间执行擦除,这意味着如果未指定绑定,则类型参数将被其最左边的边界或对象替换。因此,最左边的bound或Object确定调用重载方法的哪个版本。在运行时将哪种类型的对象传递给方法与重载解析完全无关。

如果你看一下编译的字节码,你可以看到这个:

 4  getfield Woof$Arf.yap : java.lang.Object [16]
 7  invokevirtual java.io.PrintStream.println(java.lang.Object) : void [32]
10  aload_0 [this]
11  aload_0 [this]
12  getfield Woof$Arf.yap : java.lang.Object [16]
15  invokevirtual Woof$Arf.doYapStuff(java.lang.Object) : java.lang.String [37]
18  areturn

要实现您的目标,您应该使用策略模式

public interface YapSound{
     String     doYapSound();
}

public class HappySound implements YapSound{

    @Override
    public String doYapSound() {
          return "happy";
     }

}

public class SadSound implements YapSound{

    @Override
    public String doYapSound() {
        return "sad";
    }

}

public class Arf {
    YapSound yap;
    public Arf(YapSound yap) {
        this.yap = yap;
    }

    public String woof() {
        /*
         * Should select which doYapStuff() based on whether T
         * happens to be an Integer, or something else.
         */
        return yap.doYapSound();
    }


}

public static void main(String[] args) {

    Arf arf1 = new Arf(new HappySound());
    System.out.println(arf1.woof()); // Should print "happy"

    Arf arf2 = new Arf(new SadSound());
    System.out.println(arf2.woof()); // Should print "sad"
}

答案 1 :(得分:1)

使用Integer for T实例化泛型时,由于Java使用类型擦除实现泛型的方式,T总是会覆盖Integer。