Java中桥方法的作用和真实面目

在Java的早期版本中,不支持返回类型协变(一个类如果继承/实现了父类/接口的某一个方法,那么返回类型必须与父类/接口的相同),而在Java的后期版本中(具体从几点几开始支持就不太清楚了...)开始支持返回类型协变,支持类型协变的就是Java的桥方法。
接下来列举三个场景来说明桥方法的真实面目和所起到的作用。

场景一:基本的返回类型协变问题
父类代码:

class Father{
    public Number test1(){
        return null;
    }
}

子类代码:

class Son extends Father{
    public Integer test1(){
        return null;
    }

    /**
     *  // Method descriptor #16 ()Ljava/lang/Number;
        // Stack: 1, Locals: 1
    public bridge synthetic java.lang.Number test1();
        0  aload_0 [this]
        1  invokevirtual com.cjs.gohead.generic.classorinterface.Son.test1() : java.lang.Integer [17]
        4  areturn
     */
}
子类中注释的那段即是Java编译器自动生成的桥方法,它由编译器自动生成,存在于class文件,通过那段字节码可以很清楚的看出桥方法的生成。

同时呢返回类型协变也包括泛型擦除(涉及到泛型的实现原理)所引起的一个问题:当一个“子泛型类”实现/继承自一个“父泛型类”的时候,“父泛型类”的类型信息会被擦除为限定类型,这个时候“子泛型类”里面会生成一个桥方法(场景二)。

场景二的例子(注释为编译器所生成的桥方法):

public class BridgeMethodOne {
    public static class BMOne<T> {
        // 由于擦除,类型参数T会被替换为限定类型:Object,擦除之
        // 后,就与场景一的场景相同了
        public T getT() {
            return null;
        }
    }

    public static class BMTwo extends BMOne<String> {
        /**
           // Method descriptor #16()Ljava/lang/Object;
          // Stack: 1, Locals: 1
      public bridge synthetic java.lang.Object getT();
        0  aload_0 [this]
        1  invokevirtual   learn.generic.BridgeMethodOne$BMTwo.getT() : java.lang.String [17]
    4  areturn
        */
        public String getT() {
            return null;
        }
    }
}
桥方法还可以解决另一个也是由泛型擦除所引起的问题:当使用泛型类或泛型接口的时候,当被继承/实现的方法(父类中的方法)含有方法参数的时候(并且方法参数为类型参数),由于擦除,会导致子类不会继承/实现父类的方法,所以这个时候就编译器会生成一个方法来解决这个“继承错误”的问题-方法签名不一致(场景三)。

第三个场景的例子(注释为编译器所生成的桥方法):

public class BridgeMethodTwo {
    public static class BMOne<T> {
        public T getT(T args) {
            return args;
        }
    }

    public static class BMTwo extends BMOne<String> {
        public String getT(String args) {
            return args;
        }
        /**
        // Method descriptor #18 (Ljava/lang/Object;)Ljava/lang/Object;
  // Stack: 2, Locals: 2
  public bridge synthetic java.lang.Object getT(java.lang.Object arg0);
    0  aload_0 [this]
    1  aload_1 [arg0]
    2  checkcast java.lang.String [19]
    5  invokevirtual learn.generic.BridgeMethodTwo$BMTwo.getT(java.lang.String) : java.lang.String [21]
    8  areturn
        */
    }
}
最后总结一下,Java中的桥方法可以解决返回类型协变的问题(场景一和场景二)和由泛型擦除所引起的看似“继承错误”的问题(场景三)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,这是一个常见的设计模式,Java中实现接模式的代码示例如下: ``` interface DrawAPI { public void drawCircle(int radius, int x, int y); } class RedCircle implements DrawAPI { public void drawCircle(int radius, int x, int y) { System.out.println("画红色圆形,半径为 " + radius + ", x 坐标为 " + x + ", y 坐标为 " + y); } } class GreenCircle implements DrawAPI { public void drawCircle(int radius, int x, int y) { System.out.println("画绿色圆形,半径为 " + radius + ", x 坐标为 " + x + ", y 坐标为 " + y); } } abstract class Shape { protected DrawAPI drawAPI; protected Shape(DrawAPI drawAPI){ this.drawAPI = drawAPI; } public abstract void draw(); } class Circle extends Shape { private int x, y, radius; public Circle(int x, int y, int radius, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; this.radius = radius; } public void draw() { drawAPI.drawCircle(radius,x,y); } } public class BridgePatternDemo { public static void main(String[] args) { Shape redCircle = new Circle(100,100, 10, new RedCircle()); Shape greenCircle = new Circle(100,100, 10, new GreenCircle()); redCircle.draw(); greenCircle.draw(); } } ``` 这个示例演示了一个银行展示的可能性。在这种情况下,被称为“对象”和“product”提供的不同角色中,Shape是“抽象的对象”,而DrawAPI是使用在“实现的对象”中。 BridgePatternDemo类是使用Shape和DrawAPI类的示例。 通过使用具有通用接口的导管,以及在运行时根据需要替换导管,Bridge模式将两个实体分开,因此可以相互独立地进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值