桥接方法:
Java泛型类型擦除是指在编译Java代码时,编译器会将所有的泛型类型都擦除掉,转换成原始类型。
例如,一个定义为List的泛型类型,在编译后会变成List,其中String被擦除成了Object类型。这种擦除是因为Java虚拟机不支持泛型类型的存在,因此在编译成字节码时需要将泛型类型擦除。
泛型类型擦除的结果是,编译后的字节码中不再包含泛型类型信息,因此在运行时无法获取泛型类型的具体信息。这就导致了一些限制,比如不能在运行时获取泛型类型的实际类型参数,也不能使用基本类型作为类型参数。
为了解决这些限制,Java引入了一些机制,比如桥接方法和泛型类型的边界限制,来尽可能地在编译时检查泛型类型的正确性,从而保证在运行时的安全性。
以下是一个使用泛型的例子,其中定义了一个父类和一个子类,子类继承了父类,并重写了父类中的方法:
class ParentClass<T> { public void print(T t) { System.out.println(t.toString()); } } public class ChildClass extends ParentClass<String> { @Override public void print(String s) { System.out.println("ChildClass: " + s); } }
在编译上面的代码时,Java编译器会自动生成一个桥接方法来保持父类和子类方法的方法签名一致:
class ChildClass extends ParentClass<String> { @Override public void print(String s) { System.out.println("ChildClass: " + s); } // 自动生成的桥接方法 @Override public void print(Object obj) { print((String) obj); } }
这个桥接方法的作用是将父类的方法签名转换成与子类的方法签名一致的形式。在这个例子中,父类的print方法的参数类型为T,而子类的print方法的参数类型为String,因此编译器生成的桥接方法的参数类型为Object,然后再在桥接方法中将参数强制转换成String类型,最终调用子类的print方法。
这样,在运行时调用子类的print方法时,实际上是通过桥接方法间接调用的,从而保证了泛型类型的正确性。
合成方法:
合成方法(Synthetic Method)是由编译器自动合成的方法,用于实现Java语言规范中的一些特殊语法或者操作。例如,在内部类中访问外部类的私有成员时,编译器会生成一个合成方法来完成这个访问操作。在Lambda表达式中使用局部变量时,编译器也会生成一个合成方法来捕获这个变量的值。
以下是一个使用Lambda表达式的例子,其中定义了一个函数式接口和一个使用Lambda表达式的类:
@FunctionalInterface public interface MyFunction { void run(); } public class LambdaTest { public static void main(String[] args) { int x = 10; MyFunction f = () -> { System.out.println(x); }; f.run(); } }
在编译上面的代码时,Java编译器会自动生成一个合成方法来捕获Lambda表达式中的局部变量x:
class LambdaTest { public static void main(String[] args) { int x = 10; MyFunction f = LambdaTest$$Lambda$1(x); f.run(); } // 自动生成的合成方法 private static MyFunction LambdaTest$$Lambda$1(int x) { return new MyFunction() { private final int val$x; public void run() { System.out.println(val$x); } { val$x = x; } }; } }
这个合成方法的作用是在Lambda表达式中捕获了局部变量x的值,然后在Lambda表达式实现的匿名内部类中使用。在这个例子中,Lambda表达式中使用了局部变量x,而局部变量在Lambda表达式外部定义,因此需要生成一个合成方法来捕获x的值,并将这个值传递给Lambda表达式实现的匿名内部类。
这样,在运行时调用Lambda表达式时,实际上是通过合成方法间接调用的,从而实现了在Lambda表达式中捕获外部变量的功能。