方法的调用从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
通过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(); } }