首先我要承认,我并不是一名Java程序员。我来自C ++的观点,看着别人的代码,并想知道......究竟是如何运作的?
C ++确实有模板化的成员函数,类似于Java的泛型方法,但是 C ++模板化成员函数不能是虚拟的 - 也就是说,它们不能被覆盖。这个限制对我来说很有意义,因为虚拟(可覆盖)函数最终是函数指针,并且如果虚函数可能被模板化,那么类定义无法知道为自己生成多少函数指针。
但是Java手上的通用方法似乎完全可以被派生类覆盖。 Java如何管理它?
答案 0 :(得分:2)
据我所知,C ++编译器为给定的模板参数实例化(隐式或显式)模板函数,并且在运行时,我们为每个模板参数提供不同的函数。
在Java中,没有像通用即时或专业化这样的概念。例如,
public class MyClass{
public void <T> method(T t){ }
}
我们不能像C ++中那样实例化和专门化它
public void <Integer> MyClass::method(Integer t){ //not valid in Java
//...
}
取而代之的是type erasure,所以在运行时我们有一个可以覆盖的泛化方法的单一版本。
看看这个简单的课程:
public class Main{
public static void main(String[] args){
Main m = new Main();
m.method(10);
m.methodNumber(10);
m.methodNumberAnd(10);
m.methodNumberAnd2(10);
m.methodInteger(10);
}
public <T> void method(T t){ }
public <T extends Number> void methodNumber(T t){ }
public <T extends Number & java.io.Serializable> void methodNumberAnd(T t){ }
public <T extends java.io.Serializable & java.lang.Comparable<T>> void methodNumberAnd2(T t){ }
public void methodInteger(Integer t){ }
}
并且运行
javac Main.java
javap -c Main.class
它给出了这个:
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class Main
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: bipush 10
11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokevirtual #5 // Method method:(Ljava/lang/Object;)V
17: aload_1
18: bipush 10
20: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
23: invokevirtual #6 // Method methodNumber:(Ljava/lang/Number;)V
26: aload_1
27: bipush 10
29: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
32: invokevirtual #7 // Method methodNumberAnd:(Ljava/lang/Number;)V
35: aload_1
36: bipush 10
38: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
41: invokevirtual #8 // Method methodNumberAnd2:(Ljava/io/Serializable;)V
44: aload_1
45: bipush 10
47: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
50: invokevirtual #9 // Method methodInteger:(Ljava/lang/Integer;)V
53: return
public <T> void method(T);
Code:
0: return
public <T extends java.lang.Number> void methodNumber(T);
Code:
0: return
public <T extends java.lang.Number & java.io.Serializable> void methodNumberAnd(T);
Code:
0: return
public <T extends java.io.Serializable & java.lang.Comparable<T>> void methodNumberAnd2(T);
Code:
0: return
public void methodInteger(java.lang.Integer);
Code:
0: return
}
注意已编译方法的签名:
Method method:(Ljava/lang/Object;)V
Method methodNumber:(Ljava/lang/Number;)V
Method methodNumberAnd:(Ljava/lang/Number;)V
Method methodNumberAnd2:(Ljava/io/Serializable;)V
Method methodInteger:(Ljava/lang/Integer;)V
正如@Eugene所说,在&
的情况下,泛型类型将擦除为第一个类型绑定。
答案 1 :(得分:2)
C ++模板需要根据实际参数类型生成实际类。
Java旨在生成一个基于Object的参数类。在运行时只保留Object - 这称为类型擦除。
public class A<T> {
public T f(T x) { return x; }
}
public class B extends A<String> {
@Override
public String f(String s) { return s.toLowerCase(); }
}
生成的内容如下:
public class A {
public Object f(Object x) { return x; }
}
public class B extends A {
@Override
public Object f(Object x) { return B.this.f((String)x); }
public String f(String s) { return s.toLowerCase(); }
}
C ++有一个虚拟方法表,用于将方法指针替换为类的方法指针。
Java有一个.class文件,它更像是一个C ++ .o或.obj,包含方法的“链接”信息:它们的签名:名称和完整参数类型名称。 “链接”发生在类加载上。然后jvm字节码方法调用操作可以处理继承。