方法的调用从Attr类的visitApply()方法进入,如下:

     /** Visitor method for method invocations.
     *  NOTE: The method part of an application will have in its type field
     *        the return type of the method, not the method's type itself!
     */
    public void visitApply(JCMethodInvocation tree) {
        // The local environment of a method application is
        // a new environment nested in the current one.
        Env<AttrContext> localEnv = env.dup(tree, env.info.dup());

        // The types of the actual method arguments.
        List<Type> argtypes;

        // The types of the actual method type arguments.
        List<Type> typeargtypes = null;

        Name methName = TreeInfo.name(tree.meth);

        boolean isConstructorCall = methName == names._this || methName == names._super;

        if (isConstructorCall) {
               // 构造函数的调用
        } else {
              // 非构造函数的调用
        }
        chk.validate(tree.typeargs, localEnv);
    }  

关于JCMethodInvocation的语法结构可查看:http://www.cnblogs.com/extjs4/p/7118730.html

由于构造函数也是一种特殊的函数,所以通过调用TreeInfo.name()方法获取methName后通过与this和super关键字的比较,将构造函数与一般的函数分别进行逻辑判断。

(1)调用构造函数

先来看构造函数的判断逻辑:

 // We are seeing a ...this(...) or ...super(...) call.
            // Check that this is the first statement in a constructor.
            // 对...this(...) 或者...super(...)的调用必须是构造器中的第一个语句
            if (checkFirstConstructorStat(tree, env)) {
                // Record the fact that this is a constructor call (using isSelfCall).
                localEnv.info.isSelfCall = true;

                // Attribute arguments, yielding list of argument types.
                argtypes = attribArgs(tree.args, localEnv);
                typeargtypes = attribTypes(tree.typeargs, localEnv);

                // Variable `site' points to the class in which the called constructor is defined.
                Type site = env.enclClass.sym.type;
                if (methName == names._super) {
                    // 在Object类的构造函数中不能有super()调用,因为Object是所有类的超类,没有父类
                    if (site == syms.objectType) {
                        log.error(tree.meth.pos(), "no.superclass", site);
                        site = types.createErrorType(syms.objectType);
                    } else {
                        site = types.supertype(site);
                    }
                }
                if (site.tag == CLASS) {
                    Type encl = site.getEnclosingType();
                    while (encl != null && encl.tag == TYPEVAR) {
                        encl = encl.getUpperBound();
                    }
                    if (encl.tag == CLASS) {
                        if (tree.meth.getTag() == JCTree.SELECT) { // we are calling a nested class
                            JCTree qualifier = ((JCFieldAccess) tree.meth).selected;
                            // We are seeing a prefixed call, of the form  <expr>.super(...).
                            // Check that the prefix expression conforms to the outer instance type of the class.
                            Type type = attribExpr(qualifier, localEnv,encl);
                            chk.checkRefType(qualifier.pos(),type);  // 检查type是否为引用类型
                         } else if (methName == names._super) { // ???
                             // qualifier omitted; check for existence of an appropriate implicit qualifier.
                             rs.resolveImplicitThis(tree.meth.pos(),localEnv, site, true);
                          }
                    } else if (tree.meth.getTag() == JCTree.SELECT) {
                        // 非法限定符; {0}不是内部类
                        log.error(tree.meth.pos(), "illegal.qual.not.icls",site.tsym);
                    }

                    // if we're calling a java.lang.Enum constructor,prefix the implicit String and int parameters
                    if (site.tsym == syms.enumSym && allowEnums) {
                        argtypes = argtypes.prepend(syms.intType).prepend(syms.stringType);
                    }

                    // Resolve the called constructor under the assumption that we are referring to a superclass instance of the
                    // current instance (JLS ???).
                    boolean selectSuperPrev = localEnv.info.selectSuper;
                        localEnv.info.selectSuper = true; // 在super()的调用环境下要设置selectSuper = true
                        localEnv.info.varArgs = false; // ???
                        // 查找到这个要调用的super() 构造函数
                        Symbol sym = rs.resolveConstructor(tree.meth.pos(), localEnv, site, argtypes, typeargtypes);
                    localEnv.info.selectSuper = selectSuperPrev;

                    // Set method symbol to resolved constructor...
                    TreeInfo.setSymbol(tree.meth, sym);

                    // ...and check that it is legal in the current context.
                    // (this will also set the tree's type)
                    Type mpt = newMethTemplate(argtypes, typeargtypes);
                    checkId(tree.meth, site, sym, localEnv, MTH,mpt, tree.varargsElement != null);
                } // end (site.tag == CLASS)
                // Otherwise, `site' is an error type and we do nothing
            }
            result = tree.type = syms.voidType; // 构造函数做为特殊的方法,其返回类型为void

e.g 1

首先来了解一下内隐类,《Java编程思想》有这么一段话,如下:

由于inner class的构造函数必须连接到一个reference指向outer class对象身上,所以当你继承inner class时,事情便稍微复杂些。问题出在“指向outer class对象”的那个神秘reference必须被初始化。但derived class之内不存在可连接的缺省对象,这个问题的答案是,使用专用语法,明确产生该关联性。 

class InnerA {
    class InnerB {
        class innerC {

        }
    }
}

class InheritInner extends InnerA.InnerB.innerC {
    InheritInner(InnerA.InnerB wi) {
        wi.super();
    }
}

e.g 2

// 生成x0.super()类型的东西
class A {
	class B {
		public B(String name){}
	}
}
class C{
	public void test(){
		new A().new B("dd"){
			public void m1(){

			}
		};
	}
}

e.g 3

class Outer {
	class A {
		class B extends A {
			public B(String name) {
				super();
			}
		}
	}
}

另外还需要知道Enum枚举类的构造函数,如下:

 /**
     * Sole constructor.  Programmers cannot invoke this constructor.
     * It is for use by code emitted by the compiler in response to enum type declarations.
     *
     * @param name - The name of this enum constant, which is the identifier used to declare it.
     * @param ordinal - The ordinal of this enumeration constant (its position
     *         in the enum declaration, where the initial constant is assigned  an ordinal of zero).
     */
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

下面就调用Resolev类的resolveConstructor()方法来继续处理了,方法的代码如下:

/** Resolve constructor.
     *  @param pos       The position to use for error reporting.
     *  @param env       The environment current at the constructor invocation.
     *  @param site      The type of class for which a constructor is searched.
     *  @param argtypes  The types of the constructor invocation's value  arguments.
     *  @param typeargtypes  The types of the constructor invocation's type  arguments.
     */
    Symbol resolveConstructor(DiagnosticPosition pos,
                              Env<AttrContext> env,
                              Type site,
                              List<Type> argtypes,
                              List<Type> typeargtypes) {
        Symbol sym = startResolution();
        List<MethodResolutionPhase> steps = methodResolutionSteps;

        while (steps.nonEmpty() &&
               steps.head.isApplicable(boxingEnabled, varargsEnabled) &&
               sym.kind >= ERRONEOUS
         ){
            currentStep = steps.head;
            sym = resolveConstructor(pos, env, site, argtypes, typeargtypes,
                                        steps.head.isBoxingRequired(),
                                        env.info.varArgs = steps.head.isVarargsRequired());
            // 将获取到的这一步合适的sym方法放入map中
            methodResolutionCache.put(steps.head, sym);
            steps = steps.tail;
        }

        if (sym.kind >= AMBIGUOUS) { // if nothing is found return the 'first' error
            MethodResolutionPhase errPhase = firstErroneousResolutionPhase();
            Symbol symbol = methodResolutionCache.get(errPhase);
            sym = access(symbol,pos, site, names.init, true, argtypes, typeargtypes);
            env.info.varArgs = errPhase.isVarargsRequired();
        }
        return sym;
    }

  

关于方法的筛选大概走三步,也就是Phase 1,Phase 2与Phase 3所描述的。  

15.12. Method Invocation Expressions

15.12.1. Compile-Time Step 1: Determine Class or Interface to Search
15.12.2. Compile-Time Step 2: Determine Method Signature
15.12.2.1. Identify Potentially Applicable Methods
15.12.2.2. Phase 1: Identify Matching Arity Methods Applicable by Subtyping
15.12.2.3. Phase 2: Identify Matching Arity Methods Applicable by Method Invocation Conversion
15.12.2.4. Phase 3: Identify Applicable Variable Arity Methods
15.12.2.5. Choosing the Most Specific Method
15.12.2.6. Method Result and Throws Types
15.12.2.7. Inferring Type Arguments Based on Actual Arguments
15.12.2.8. Inferring Unresolved Type Arguments
15.12.3. Compile-Time Step 3: Is the Chosen Method Appropriate?
15.12.4. Run-Time Evaluation of Method Invocation
15.12.4.1. Compute Target Reference (If Necessary)
15.12.4.2. Evaluate Arguments
15.12.4.3. Check Accessibility of Type and Method
15.12.4.4. Locate Method to Invoke
15.12.4.5. Create Frame, Synchronize, Transfer Control

  

通过MethodResolutionStep枚举类也可以看出,代码如下:

enum MethodResolutionPhase {
        BASIC(false, false),
        BOX(true, false),
        VARARITY(true, true);

        boolean isBoxingRequired;
        boolean isVarargsRequired;

        MethodResolutionPhase(boolean isBoxingRequired, boolean isVarargsRequired) {
           this.isBoxingRequired = isBoxingRequired;
           this.isVarargsRequired = isVarargsRequired;
        }

        public boolean isBoxingRequired() {
            return isBoxingRequired;
        }

        public boolean isVarargsRequired() {
            return isVarargsRequired;
        }

        // 不能够使用可变参数(varargsEnabled=false) 并且 要求可变参数时(isVarargsRequired=true) 变为不可用
        // 不能够使用装箱与拆箱(boxingEnabled=false) 并且 要求装箱与拆箱时(isBoxingRequired=true)  变为不可用
        public boolean isApplicable(boolean boxingEnabled, boolean varargsEnabled) {
            return (varargsEnabled || !isVarargsRequired) &&
                   (boxingEnabled || !isBoxingRequired);
        }
    }

也就是说:

(1)不进行拆箱装箱,没有可变参数变量的声明

(2)允许拆箱装箱,但不允许有可变参数变量的声明

(3)允许可变参数变量声明 

关于在resolveConstructor()方法中调用的如下方法:

 sym = resolveConstructor(pos, env, site, argtypes, typeargtypes,
                                        steps.head.isBoxingRequired(),
                                        env.info.varArgs = steps.head.isVarargsRequired());

后面将会有详细的说明。  

接着调用TreeInfo.setSymbol(tree.meth,sym)方法,截图如下:

也就是设置了sym属性,如上设置了MethodSymbol,setSymbol()方法的代码如下:

 /** If this tree is an identifier or a field, set its symbol, otherwise skip.
     */
    public static void setSymbol(JCTree tree, Symbol sym) {
        tree = skipParens(tree);
        switch (tree.getTag()) {
        case JCTree.IDENT:
            ((JCIdent) tree).sym = sym; break;
        case JCTree.SELECT:
            ((JCFieldAccess) tree).sym = sym; break;
        default:
        }
    }

newMethTemplate()方法的代码如下:

/** Obtain a method type with given argument types.
     */
    Type newMethTemplate(List<Type> argtypes, List<Type> typeargtypes) {
        //  public MethodType(List<Type> argtypes,Type restype,List<Type> thrown,TypeSymbol methodClass)
        MethodType mt = new MethodType(argtypes, null, null, syms.methodClass);
        // 没有类型参数时方法的Type类型为MethodType,否则为ForAll
        return (typeargtypes == null) ? mt : (Type)new ForAll(typeargtypes, mt);
    }

有一个重要的方法checkId()方法,非常重要!  

 

(2)普通方法的调用

接着来看普通方法的判断逻辑,如下:

  // Otherwise, we are seeing a regular method call.
            // Attribute the arguments, yielding list of argument types, ...
            argtypes = attribArgs(tree.args, localEnv);
            typeargtypes = attribAnyTypes(tree.typeargs, localEnv);

            // ... and attribute the method using as a prototype a methodtype
            // whose formal argument types is exactly the list of actual
            // arguments (this will also set the method symbol).
            Type mpt = newMethTemplate(argtypes, typeargtypes);
            localEnv.info.varArgs = false;
            Type mtype = attribExpr(tree.meth, localEnv, mpt);

            if (localEnv.info.varArgs) {
                Assert.check(mtype.isErroneous() || tree.varargsElement != null);
            }

            // Compute the result type.
            Type restype = mtype.getReturnType();
            if (restype.tag == WILDCARD) {
                throw new AssertionError(mtype);
            }

            // as a special case, array.clone() has a result that is the same as static type of the array being cloned
            if (tree.meth.getTag() == JCTree.SELECT &&
                allowCovariantReturns &&
                methName == names.clone &&
                types.isArray(((JCFieldAccess) tree.meth).selected.type)
             ){
                restype = ((JCFieldAccess) tree.meth).selected.type;
            }

            // as a special case, x.getClass() has type Class<? extends |X|>
            if ( allowGenerics &&
                 methName == names.getClass && // getClass
                 tree.args.isEmpty()
             ){
                Type qualifier = null;
                if(tree.meth.getTag() == JCTree.SELECT){
                    qualifier = ((JCFieldAccess) tree.meth).selected.type;
                }else{
                    qualifier = env.enclClass.sym.type;
                }

                Type a = types.erasure(qualifier);
                WildcardType wt = new WildcardType(a,BoundKind.EXTENDS,syms.boundClass);
                Type b = restype.getEnclosingType();
                restype = new  ClassType(b,List.<Type>of(wt),restype.tsym);
            }

            chk.checkRefTypes(tree.typeargs, typeargtypes);

            // Check that value of resulting type is admissible in the current context.  Also, capture the return type
            Type c = capture(restype);
            result = check(tree, c, VAL, pkind, pt);

其中有对两类方法的调用进行了特殊的处理,如array.clone()与x.getClass()。举个例子,如下:

public class Test3 {
    public static void main(String[] args) {
        int a[] = { 1, 2 };
        int b[] = a.clone();

        // 获取到的是Class类
        Class<? extends Class> m = Test3.class.getClass();
        // 获取到的是Test3类
        Class<? extends Test3> n = new Test3().getClass();

        System.out.println(m); // class java.lang.Class
        System.out.println(n); // class com.test07.Test3
        System.out.println(n instanceof Class); // true

    }
}

在进行数组克隆的判断逻辑时还需要判断allowCovariantReturns标识,由于clone方法在Object的定义如下:

 protected native Object clone() throws CloneNotSupportedException;

返回值类型为Object,而clone()后的返回类型确为int[]类型,所以需要covariant,covariant可以参考:http://www.cnblogs.com/extjs4/p/6305654.html 

而对于getClass()方法来说,如果new ClassName().getClass(),那么最后得到的返回结果为Class<? extends |ClassName|>,从如上的实例也可以看出。  

最后调用了一个重要的方法check()方法,非常重要!

e.g 1:

public class Test2{
	public void m1(){}

	public void test(){
		// javac warning: Unused type arguments for the non generic method m1() of type Test2;
		// it should not be parameterized with arguments <String>
		new Test2().<String>m1();
	}

}

  

Javac之关于方法的调用1的更多相关文章

  1. (转)为什么不能从静态的方法里面调用非静态方法,或变量and类加载机制

    1. 程序最终都将在内存中执行,变量只有在内存中占有一席之地时才能被访问. 类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问:非静态成员(变量和方法)属于类的对 ...

  2. JNI-Thread中start方法的调用与run方法的回调分析

    前言 在java编程中,线程Thread是我们经常使用的类.那么创建一个Thread的本质究竟是什么,本文就此问题作一个探索. 内容主要分为以下几个部分 1.JNI机制的使用 2.Thread创建线程 ...

  3. UIView的layoutSubviews和drawRect方法何时调用

    首先两个方法都是异步执行.layoutSubviews方便数据计算,drawRect方便视图重绘. layoutSubviews在以下情况下会被调用: 1.init初始化不会触发layoutSubvi ...

  4. 自己实现一个Native方法的调用

    JNI 开始本篇的内容之前,首先要讲一下JNI.Java很好,使用的人很多.应用极广,但是Java不是完美的.Java的不足体现在运行速度要比传统的C++慢上许多之外,还有Java无法直接访问到操作系 ...

  5. 从vs2010的UnitTestFramework类库提取私有方法反射调用的方法

    背景 年龄大点的程序员都知道在vs2010中创建单元测试非常的简单,鼠标定位在方法名字,右键创建单元测试,就会创建一个测试方法,即使是在私有方法上也可以创建测试方法. VS2010以后就没这么简单了, ...

  6. Unity3D中C#和js方法相互调用

    通过查找资料,Unity3D中C#和js要相互调用彼此的方法,js文件必须放在"Standard Assets". "Pro Standard Assets" ...

  7. Java笔记4-do while循环,break,修饰符,方法的调用

    do while循环语法:do{ //循环体}while(条件表达式); 注:它是先执行循环体,后再判断的循环结构. 如:int i = 0;do{ System.out.println(" ...

  8. paip。java 高级特性 类默认方法,匿名方法+多方法连续调用, 常量类型

    paip.java 高级特性 类默认方法,匿名方法+多方法连续调用, 常量类型 作者Attilax 艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http ...

  9. 虚方法的调用是怎么实现的(单继承VS多继承)

    我们知道通过一个指向之类的父类指针可以调用子类的虚方法,因为子类的方法会覆盖父类同样的方法,通过这个指针可以找到对象实例的地址,通过实例的地址可以找到指向对应方法表的指针,而通过这个方法的名字就可以确 ...

随机推荐

  1. Base64编码说明

    Base64编码说明 Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式. 如果剩下的字符不足3个字节,则用0填充 ...

  2. 层层递进Struts1(五)之处理流程

    这篇博客我们深入Struts框架执行部分源码,从ActionServlet的process函数开始,看一下其内在的执行过程. 流程图 以下流程图展示的是ActionServlet和RequestPro ...

  3. Internal error(U783)

    今天打开代码时一个单元文件报这个错误Internal error(U783),不知道什么原因,然后多次关闭打开后,又没报这个错误了,记录下 http://www.aiuxian.com/article ...

  4. 介绍自己,并介绍github注册过程和初步使用

    我是一名南通大学的学生,我叫李可,学号是1413042029,班级:网络工程141,我是一名网络工程专业的学生,我一般喜欢看看课外书. 现在我介绍一下我注册github的过程: 1.登陆https:/ ...

  5. MySQL简单实现多字段模糊查询

    我所做的商城项目前些时提了新需求,要求前台搜索商品除了能通过商品名称搜索到以外,还可以通过别个信息搜索,比如:商品编号.详情内容描述等等,类似于全文搜索了.我首先想到的就是lucene,但是对代码这样 ...

  6. 查看jar包的jdk版本并降级

    用解压工具打开jar包(例子都是用7zip)   进入到META-INF目录,查看MANIFEST.MF文件,查看Bulid-Jdk,下图就为1.7.0_55版本的JDK,这就表示jetty-serv ...

  7. c#进阶之lambda表达式

    阅读之前,先确保对委托有基本的了解,传送门 c#进阶之浅析委托和事件. lambda表达式雏形第一步 在委托那篇文章,绑定的的方法都是具名函数,为了简化书写,可以换成匿名函数 public deleg ...

  8. 在VC++中执行VBS代码

    此代码来自https://blog.csdn.net/zhu2695/article/details/13770671 作者: zhu2695   时间:2013年10月31日 13:08:41 #i ...

  9. 【OCP题库】最新CUUG OCP 12c 071考试题库(68题)

    68.(29-13)choose two: Which two statements are true? (Choose two.) A) DICTIONARY is a view that cont ...

  10. Jzoj 初中2249 蒸发学水(并查集)

    题目描述 众所周知,TerryHu 是一位大佬,他平时最喜欢做的事就是蒸发学水. 机房的位置一共有n 行m 列,一开始每个位置都有一滴学水,TerryHu 决定在每一个时刻选择 一滴学水进行蒸发,直到 ...