zoukankan      html  css  js  c++  java
  • ABAP,Java和JavaScript类的构造函数使用的一些陷阱

    This question draws my attention during a discussion with my colleague recently.
    I will explain it in ABAP, Java and JavaScript.

    In ABAP

    I have a super class and a sub class.
    Source code for super class:

    class ZCL_SUPER definition
      public
      create public .
    public section.
      methods CONSTRUCTOR .
      methods SET_I
        importing
          !IV_I type INT4 .
    protected section.
    private section.
      data MV_SUPER type INT4 .
    ENDCLASS.
    CLASS ZCL_SUPER IMPLEMENTATION.
      method CONSTRUCTOR.
        me->set_i( 100 ).
      endmethod.
      method SET_I.
        me->mv_super = iv_i.
      endmethod.
    ENDCLASS.
    

    In constructor of super class, method set_i is called to set the member attribute mv_super to 100.
    And here is my sub class:

    class ZCL_SUB definition
      public
      inheriting from ZCL_SUPER
      final
      create public .
    public section.
      methods PRINT .
      methods SET_I
        redefinition .
    protected section.
    private section.
      data MV_SUB type I value 1 ##NO_TEXT.
    ENDCLASS.
    CLASS ZCL_SUB IMPLEMENTATION.
      method PRINT.
        WRITE: / ' sub:' , mv_sub.
      endmethod.
      METHOD set_i.
        super->set_i( iv_i = iv_i ).
        me->mv_sub = iv_i.
        WRITE: / 'mv_sub assigned by: ' , iv_i.
      ENDMETHOD.
    ENDCLASS.
    

    The redefinition of set_i is done in order to fill the member attribute mv_sub in sub class.
    And now in report, use this line for test:

    NEW zcl_sub( )->print( ).
    Originally I expect the following lines will be printed:

    mv_sub assigned by: 100
    sub: 100
    

    I am wrong. The actual output:

    sub: 1
    When debugging into the code, we can find the reason:

    in constructor, the redefinition of set_i done by sub class is not called at all, since the technical type of me reference points to super class. This makes sense since the execution context is constructor and the initialization of super class is not finished yet.

    In Java

    Let’s now see what will happen in Java.
    Super class:

    public class SuperClass {
    
    	private int mSuperX;
    
    	public SuperClass() {
    		setX(99);
    	}
    
    	public void setX(int x) {
    		mSuperX = x;
    	}
    }
    

    Sub class:

    public class SubClass extends SuperClass {
    
    	private int mSubX = 1;
    
    	public SubClass() {}
    
    	@Override
    	public void setX(int x) {
    		super.setX(x);
    		mSubX = x;
    		System.out.println("SubX is assigned " + x);
    	}
    
    	public void printX() {
    		System.out.println("SubX = " + mSubX);
    	}
    }
    

    test:

    public static void main(String[] args) {
    		SubClass sc = new SubClass();
    		sc.printX();
    	}
    

    Output:

    The constructor execution behavior is completely different in Java: in constructor, the redefinition of setX done in sub class now gets chance to be called. However, it is overwritten to default value 1 later.
    Use Javap to analyze the byte code.
    The magic of initialization lays in line 59: invokespecial #18.

    the byte code clearly shows the Java code new SubClass() will invoke SubClass.””.
    The #18 represents the entry in constant pool with id 18, which is SubClass.:

    So use Javap to review byte code of sub class again.

    Here explains why in the last line of output, subX is reset to 1, since the initialization of it is done AFTER super class’ constructor call.

    What logic is contained in SuperClass.””? Inspect SuperClass.”” again:

    12 points to SuperClass.setX:

    Byte code of setX:
    line 59: put this reference to stack
    line 60: push parameter x of method setX to stack
    line 61: put stack top element to variable #20, which is SuperClass.mSuperX:


    The execution sequence analyzed so far:
    SubClass.”” -> SuperClass.”” -> SubClass.setX(int) -> SuperClass.setX(int)
    Which could clearly be observed in callstack:

    In JavaScript

    I write an example via JavaScript to illustrate the execution logic in above example.
    Super class:

    function SuperClass(){
    	this.setX(99);
    }
    
    SuperClass.prototype = {
        mSuperX : 0,
        setX : function(x){
             this.mSuperX = x;
        }
    };
    

    Sub class:

    function SubClass(){  
        SuperClass.call(this);  
        this.mSubX = 1;
    }  
    
    SubClass.prototype = new SuperClass();  
    
    SubClass.prototype.setX = function(x){
        	SuperClass.prototype.setX(x);
    		this.mSubX = x;
    		console.log("SubX is assigned " + x);
    };
    
    SubClass.prototype.print = function(){
    	console.log("SubX: " + this.mSubX);
    }
    

    Test code:

    var sub = new SubClass();
    sub.print();
    

    Let’s step into code of new SubClass():

    (1) it will call SuperClass’ constructor:

    (2) In Super class constructor, since now this points to SubClass, so the redefinition of setX is called:

    (3) In redefined setX in SubClass, line 23 and line 24 will set attribute mSuperX in SuperClass and mSubX in Sub class accordingly.

    (4) finally:

    So we get the same output in Java:

    Conclusion

    If you call a non-final method in constructor and that method is redefined by sub class, be careful that your code might not work as you expect when you create a new instance of sub class:

    (1) In ABAP, the redefinition in sub class will NOT be called in constructor.
    (2) In Java, the constructor of super class is called BEFORE the initialization of sub class member attribute.

    要获取更多Jerry的原创文章,请关注公众号"汪子熙":

  • 相关阅读:
    rails s 命令不起作用
    ubuntu下virtualbox共享usb
    ubuntu15.04 无法识别exfat格式
    .net core 2.2 修改IdentityUser主键标识类型
    Mac os 安装node.js及环境变量的配置过程
    常见互联网网络名词整理
    assert的用法
    Mac系统中 改变 pip总是默认安装在Mac上自带的python上为python3
    测试工程师的发展之路
    MySQL的mysql-8.0.17-winx64版本安装过程中遇到的问题
  • 原文地址:https://www.cnblogs.com/sap-jerry/p/13598777.html
Copyright © 2011-2022 走看看