Javac之关于方法的调用1
方法的调用从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(); } }
Javac之关于方法的调用1的更多相关文章
- (转)为什么不能从静态的方法里面调用非静态方法,或变量and类加载机制
1. 程序最终都将在内存中执行,变量只有在内存中占有一席之地时才能被访问. 类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问:非静态成员(变量和方法)属于类的对 ...
- JNI-Thread中start方法的调用与run方法的回调分析
前言 在java编程中,线程Thread是我们经常使用的类.那么创建一个Thread的本质究竟是什么,本文就此问题作一个探索. 内容主要分为以下几个部分 1.JNI机制的使用 2.Thread创建线程 ...
- UIView的layoutSubviews和drawRect方法何时调用
首先两个方法都是异步执行.layoutSubviews方便数据计算,drawRect方便视图重绘. layoutSubviews在以下情况下会被调用: 1.init初始化不会触发layoutSubvi ...
- 自己实现一个Native方法的调用
JNI 开始本篇的内容之前,首先要讲一下JNI.Java很好,使用的人很多.应用极广,但是Java不是完美的.Java的不足体现在运行速度要比传统的C++慢上许多之外,还有Java无法直接访问到操作系 ...
- 从vs2010的UnitTestFramework类库提取私有方法反射调用的方法
背景 年龄大点的程序员都知道在vs2010中创建单元测试非常的简单,鼠标定位在方法名字,右键创建单元测试,就会创建一个测试方法,即使是在私有方法上也可以创建测试方法. VS2010以后就没这么简单了, ...
- Unity3D中C#和js方法相互调用
通过查找资料,Unity3D中C#和js要相互调用彼此的方法,js文件必须放在"Standard Assets". "Pro Standard Assets" ...
- Java笔记4-do while循环,break,修饰符,方法的调用
do while循环语法:do{ //循环体}while(条件表达式); 注:它是先执行循环体,后再判断的循环结构. 如:int i = 0;do{ System.out.println(" ...
- paip。java 高级特性 类默认方法,匿名方法+多方法连续调用, 常量类型
paip.java 高级特性 类默认方法,匿名方法+多方法连续调用, 常量类型 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http ...
- 虚方法的调用是怎么实现的(单继承VS多继承)
我们知道通过一个指向之类的父类指针可以调用子类的虚方法,因为子类的方法会覆盖父类同样的方法,通过这个指针可以找到对象实例的地址,通过实例的地址可以找到指向对应方法表的指针,而通过这个方法的名字就可以确 ...
随机推荐
- EventBus事件总线框架(发布者/订阅者模式,观察者模式)
一. android应用内消息传递的方式: 1. handler方式-----------------不同线程间传递消息. 2. Interface接口回调方式-------任意两个对象. 3. In ...
- [转]RTH试用手记之“偶发信号观测”
年初,罗德与施瓦茨公司(Rohde & Schwarz)推出了第一款的手持示波器,从指标上看,该示波器打破了传统手持器功能简单.指标水平低.结构粗糙的印象,取而代之达到了主流台式数字示波器的性 ...
- [Elixir002]节点启动后自动连接其它节点
问题: 如何指定一个节点在启动后自动连接到别的节点上? 这个我们要使用到sys.config,这是erlang的配置文件,这个文件一般都是$ROOT/releases/Vsn下 1. 首先我们要先启动 ...
- [Erlang24]使用zotonic搭建网站记录
zotonic的搭建网站(blog)记录: zotonic:用Erlang做的一个web 框架: 和wordpress 类似,但是官网称比PHP CMS要快10倍以上 先看看我的成果:正弦 ...
- 关于easyui展示慢的Debug
同事开发的软件系统采用Easyui做的前台界面,当业务变得比较复杂之后,展示效果就变得很慢,于是我开始了原因的排查,现在已经找到了具体的原因,所以拿出来与大家一起分享调试过程. 既然调试的是前端,那么 ...
- 水平居中和transform: translateY(-50%) 实现元素垂直居中效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- deepin配置反向代理映射本地到公网
这里我是用的小米球的免费ngrok 相信deepin的新用户在配置反向代理时,会感觉到一脸茫然,因为一开始我也是这样,但经过短暂的了解了deepin后,发现,其实与在Debian上配置并没有什么区别! ...
- 【javascript】—— JS判断浏览器类型、操作系统
navigator.userAgent : userAgent 属性是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值. navigator.platform : platform ...
- CentOS7打开关闭防火墙与端口
http://www.javahelp.com.cn/h-nd-747.html#_np=153_1707
- 浅析Postgres中的并发控制(Concurrency Control)与事务特性(下)
上文我们讨论了PostgreSQL的MVCC相关的基础知识以及实现机制.关于PostgreSQL中的MVCC,我们只讲了元组可见性的问题,还剩下两个问题没讲.一个是"Lost Update& ...