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的原创文章,请关注公众号"汪子熙":

  • 相关阅读:
    JSTL之迭代标签库
    java中的IO流
    浅谈代理模式
    TreeSet集合
    网络编程之UDP协议
    Java中的多线程
    Java中的面向对象
    JavaScript 函数表达式
    JavaScript 数据属性和访问器属性
    JavaScript 正则表达式语法
  • 原文地址:https://www.cnblogs.com/sap-jerry/p/13598777.html
Copyright © 2011-2022 走看看