5. Conversions and Promotions

5.1. Kinds of Conversion
5.1.1. Identity Conversion
5.1.2. Widening Primitive Conversion
5.1.3. Narrowing Primitive Conversion
5.1.4. Widening and Narrowing Primitive Conversion
5.1.5. Widening Reference Conversion
5.1.6. Narrowing Reference Conversion
5.1.7. Boxing Conversion
5.1.8. Unboxing Conversion
5.1.9. Unchecked Conversion
5.1.10. Capture Conversion
5.1.11. String Conversion
5.1.12. Forbidden Conversions
5.1.13. Value Set Conversion
如上11种基本类型的转换
5.2. Assignment Conversion
5.3. Method Invocation Conversion
5.4. String Conversion
5.5. Casting Conversion
5.5.1. Reference Type Casting
5.5.2. Checked Casts and Unchecked Casts
5.5.3. Checked Casts at Run Time
5.6. Numeric Promotions
5.6.1. Unary Numeric Promotion
5.6.2. Binary Numeric Promotion

如上5种转换上下文环境,每种环境可能允许一些基本类型转换

1、isConvertible()

 /**
     * Is t a subtype of or convertible via boxing/unboxing conversion to s?
     */
    public boolean isConvertible(Type t, Type s, Warner warn) {
        if (t.tag == ERROR)
            return true;
        boolean tPrimitive = t.isPrimitive();
        boolean sPrimitive = s.isPrimitive();
        if (tPrimitive == sPrimitive) {
            return isSubtypeUnchecked(t, s, warn);
        }

        if (!allowBoxing)
            return false;

        return tPrimitive
            ? isSubtype(boxedClass(t).type, s)
            : isSubtype(unboxedType(t), s);
    }  

当两个类型相同时(同时为原始类型或引用类型)通过调用isSubtypeUnchecked()方法来进一步判断。

 /**
     * Is t an unchecked subtype of s?
     */
    public boolean isSubtypeUnchecked(Type t, Type s, Warner warn) {
        boolean result = isSubtypeUncheckedInternal(t, s, warn);
        if (result) {
            checkUnsafeVarargsConversion(t, s, warn);
        }
        return result;
    }  

在如上方法中涉及到两个重要的方法isSubtypeUncheckedInternal()与checkUnsafeVarargsConversion(),不过优先调用前一个方法,如果是Unchecked Subtype关系,则需要检查是否为Unsafe Varargs Conversion。

private boolean isSubtypeUncheckedInternal(Type t, Type s, Warner warn) {
        if (t.tag == ARRAY && s.tag == ARRAY) {
            if (((ArrayType)t).elemtype.tag <= lastBaseTag) {
                return isSameType(elemtype(t), elemtype(s));
            } else {
                return isSubtypeUnchecked(elemtype(t), elemtype(s), warn);
            }
        }
        else if (isSubtype(t, s)) {
            return true;
        }
        else if (t.tag == TYPEVAR) {
            return isSubtypeUnchecked(t.getUpperBound(), s, warn);
        }
        else if (s.tag == UNDETVAR) {
            UndetVar uv = (UndetVar)s;
            if (uv.inst != null)
                return isSubtypeUnchecked(t, uv.inst, warn);
        }
        else if (!s.isRaw()) {
            Type t2 = asSuper(t, s.tsym);
            if (t2 != null && t2.isRaw()) {
                if (isReifiable(s))
                    warn.silentWarn(LintCategory.UNCHECKED);
                else
                    warn.warn(LintCategory.UNCHECKED);
                return true;
            }
        }
        return false;
    }

当两个类型是数组类型时,并且是原始数组类型时,则数组类型的元素类型必须相同,也就是byte[]与int[]没有父子类关系,如下代码会报错:

byte[] x = new byte[10];
int[] y = (int[])x;

如果数组类型的元素非原始类型,那么需要进一步比较数组中的元素类型。获取数组元素类型通过如下方法来完成。

/**
     * The element type of an array.
     */
public Type elemtype(Type t) {
        switch (t.tag) {
        case WILDCARD:
            return elemtype(upperBound(t));
        case ARRAY:
            return ((ArrayType)t).elemtype;
        case FORALL:
            return elemtype(((ForAll)t).qtype);
        case ERROR:
            return t;
        default:
            return null;
        }
}

需要说明的是类型变量的上界也可能是数组类型,虽然编译器好像不支持这么做,但是JLS7中确实有这么规定。如果是类型变量,那么就取上界后继续查找数组元素的类型。

而ForAll类型不太明白??  

当t的类型为类型变量时,如下:

public class Test<T extends FilterInputStream> {
	public void test(T dd) {
		InputStream p = (InputStream)dd;
	}
}

而当s为UNDETVAR时,不太明白??

下面的逻辑需要明白如下两个概念:  

4.7. Reifiable Types

4.8. Raw Types 

举几个是Reifiable Types的例子,如下:

class A{}
class B<T>{}
class C<T>{
	class D<X>{

	}
}

class TestType{
	public void test(){
		//It refers to a non-generic class or interface type declaration.
		A a;
		// It is a parameterized type in which all type arguments are unbounded wildcards
		B<?> b;
		// It is a primitive type
		int c;
		// It is an array type (§10.1) whose element type is reifiable.
		int[] d;
		// It is a nested type where, for each type T separated by a ".", T itself is reifiable.
		C<?>.D<?> e;
		// It is a raw type
	}
}

举几个是Raw Types的例子,如下:

class A{}
class B<T>{}
class C<T>{
	class D<X>{

	}
	class E{
		T e;
	}
}

class TestType{
	public void test(){
		// A non-generic class or interface type is not a raw type.
		A a;
		// The reference type that is formed by taking the name of a generic type declaration
		// without an accompanying type argument list.
		B b;
		// An array type whose element type is a raw type.
		B[] c;
		// A non-static member type of a raw type R that is not inherited from a superclass or superinterface of R
		C.D d;
		C.E e;
	}
}  

现在来理解如下的代码:

 else if (!s.isRaw()) {
            Type t2 = asSuper(t, s.tsym); // 根据s.tsym符号来查找t类型
            if (t2 != null && t2.isRaw()) {
                if (isReifiable(s))
                    warn.silentWarn(LintCategory.UNCHECKED);
                else
                    warn.warn(LintCategory.UNCHECKED);
                return true;
            }
}

回到isSubtypeUnchecked()方法继续来看另外一个方法,代码如下:

 private void checkUnsafeVarargsConversion(Type t, Type s, Warner warn) {
        // t为非数组或者t是数组,但是是一个reifiable的数组
        if (t.tag != ARRAY || isReifiable(t))
            return;

        // t是一个数组并且是一个非reifiable的数组
        ArrayType from = (ArrayType)t;
        boolean shouldWarn = false;
        switch (s.tag) {
            case ARRAY:
                ArrayType to = (ArrayType)s;
                shouldWarn = from.isVarargs() &&
                        !to.isVarargs() &&
                        !isReifiable(from);
                break;
            case CLASS:
                shouldWarn = from.isVarargs();
                break;
        }
        if (shouldWarn) {
            warn.warn(LintCategory.VARARGS);
        }
    }

举个例子,如下:

public void test2(){

		Map<String, Object> row1 = new HashMap<String, Object>();
		Map<String, Object> row2 = new HashMap<String, Object>();
		// Type safety: A generic array of Map<String,Object> is created for a varargs parameter
		mockInvokeDBHandler(row1, row2);

}

private void mockInvokeDBHandler(Map<String, Object>... rows) {
	    List<Map<String, Object>> allRows = Arrays.asList(rows);

	    Object o = (Serializable)rows;
	    // rest of method omitted
}

在调用方法时会将可变参数转换为数组类型,所以调用mockInvokeDBHandler()方法会给出警告。而将rows转换为Serializable类时同样会给出警告。  

再举个例子,如下:

public class Test {
	public <T extends ArrayList> void test(T dd) {
		// Type safety: The expression of type T needs unchecked conversion to conform to List<String>
		List<String> l = dd;
	}
}

在将dd赋值给List<String>类型时会调用isSubtypeUncheckedInternal()方法。

  

2、isCastable()

/**
     * Is t is castable to s?
     * s is assumed to be an erased type.
     * (not defined for Method and ForAll types).
     */
    public boolean isCastable(Type t, Type s, Warner warn) {
        if (t == s) {
            return true;
        }

        // 由于类型只有原始类型与引用类型两种,如果不相等,则必有一个原始类型,一个引用类型
        if (t.isPrimitive() != s.isPrimitive()) {
            if(!allowBoxing){
                return false;
            }
            if(isConvertible(t, s, warn)){
                return true;
            }
            /*
               e.g
                 Object a = 2;
                 int b = (int)a;
            */
            if( allowObjectToPrimitiveCast &&
                    s.isPrimitive() &&
                    isSubtype(boxedClass(s).type, t)){
                return true;
            }
            return false;
        }

        if (warn != warnStack.head) {
            try {
                warnStack = warnStack.prepend(warn);
                checkUnsafeVarargsConversion(t, s, warn);
                return isCastable.visit(t,s);
            } finally {
                warnStack = warnStack.tail;
            }
        } else {
            return isCastable.visit(t,s);
        }
    }

  

下面看一下checkUnsafeVarargsConversion()方法,代码如下:

 /*
        public  static <E> void addAll(E... array) {  // varargs warning
            // 如下的两个转换会调用下面的方法
            Integer[] e = (Integer[])array;
            Serializable s = (Serializable)array;
        }
     */
    private void checkUnsafeVarargsConversion(Type t, Type s, Warner warn) {
        // t 为非数组或者t是数组,但是是一个reifiable的数组
        if (t.tag != ARRAY || isReifiable(t))
            return;

        // t 是一个数组并且是一个非reifiable的数组
        ArrayType from = (ArrayType)t;
        boolean shouldWarn = false;
        switch (s.tag) {
            case ARRAY:
                ArrayType to = (ArrayType)s;
                shouldWarn = from.isVarargs() &&
                        !to.isVarargs() &&
                        !isReifiable(from);
                break;
            case CLASS:
                shouldWarn = from.isVarargs();
                break;
        }
        if (shouldWarn) {
            warn.warn(LintCategory.VARARGS);
        }
    }

  

重点看一下isCastable这个匿名类对象提供的一些访问者方法。

(1)visitType()方法

        public Boolean visitType(Type t, Type s) {
            if (s.tag == ERROR) {
                return true;
            }

            switch (t.tag) {
            case BYTE: case CHAR: case SHORT: case INT: case LONG: case FLOAT:
            case DOUBLE:
                return s.tag <= DOUBLE;
            case BOOLEAN:
                return s.tag == BOOLEAN;
            case VOID:
                return false;
            case BOT:
                return isSubtype(t, s);
            default:
                throw new AssertionError();
            }
        }

e.g

 byte a = 2;
Integer b = (int)a; // 则type转换为int时需要调用visitType()方法

  

(2)visitWildcardType()方法  

@Override
public Boolean visitWildcardType(WildcardType t, Type s) {
            return isCastable(upperBound(t), s, warnStack.head);
}

  

Types方法之isCastable-isConvertible的更多相关文章

  1. Types方法之upperBound-lowerBound-isUnbounded-containsType

    1.upperBound(Type t)方法 /** * The "rvalue conversion". * The upper bound of most types is t ...

  2. Types方法之isSameType-isSuperType-isSubType

    4.isSameType() 方法 /** * Is t the same type as s? */ public boolean isSameType(Type t, Type s) { retu ...

  3. runtime第三部分方法和消息

    接上一篇http://www.cnblogs.com/ddavidXu/p/5924049.html 转载来源http://www.jianshu.com/p/6b905584f536 http:// ...

  4. Runtime 动态加载方法

    动态加载 #import"ViewController.h" #import"Person.h" @interfaceViewController() @end ...

  5. Objective-C Runtime 运行时之三:方法与消息

    基础数据类型 SEL SEL又叫选择器,是表示一个方法的selector的指针,其定义如下: typedef struct objc_selector *SEL; objc_selector结构体的详 ...

  6. Objective-C Runtime 运行时之三:方法与消息(转载)

    前面我们讨论了Runtime中对类和对象的处理,及对成员变量与属性的处理.这一章,我们就要开始讨论Runtime中最有意思的一部分:消息处理机制.我们将详细讨论消息的发送及消息的转发.不过在讨论消息之 ...

  7. iOS运行时使用(动态添加方法)

    1 举例  我们实现一个Person类 然后Person 其实是没得对象方法eat:的 下面调用person的eat方法 程序是会奔溃的 那么需要借助运行时动态的添加方法 Person *p = [[ ...

  8. 快速上手Runtime(四)之动态添加方法

    如果一个类方法非常多,加载类到内存的时候也比较耗费资源,可以使用动态给某个类,添加方法解决.做到优化内存,节省资源的效果. // // Person.m // ResolveInstanceMetho ...

  9. iOS---runtime介绍

    本文目录 1.Runtime简介 2.Runtime相关的头文件 3.技术点和应用场景 3_1.获取属性\成员变量列表 3_2.交换方法实现 3_3.类\对象的关联对象,假属性 3_4.动态添加方法, ...

随机推荐

  1. 试题 E: 迷宫 第十届蓝桥杯

    [问题描述]下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可以通行的地方.010000000100001001110000迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个 ...

  2. 咏南中间件新增SQL日志

    为了方便开发时跟踪调试SQL语句的执行情况,咏南中间件新增SQL日志,所有执行过的SQL都会写入SQL日志文件中. SQLDEBUG设为1,启用:设为0,停止写SQL日志.

  3. Android-GsonUtil-工具类

    GsonUtil-工具类 是把Google提供的Gons进行了方法封装,提供了关于一些常用的Gons使用的公共方法: package common.library.utils; import andr ...

  4. AndroidStudio-Unable to save settings Failed to save settings. Please restart Android Studio

    Unable to save settings Failed to save settings. Please restart Android Studio 解决方法: 删除工程的.idea 然后在 ...

  5. 博客和Github简单练习

    我的第一篇博客 1.首先是自我介绍 姓名:孙弘毅 班级:网工142 学号:1413042050 兴趣:游戏,看书 至于我写了多少代码我也不清楚,反正不多 2.Github  注册流程 (1)百度Git ...

  6. Python学习-13.Python的输入输出(二)

    在Python中,读取文件使用open函数 file=open(r'E:\temp\test.txt','r') var = file.read() print(var) file.close() 第 ...

  7. 国际时区 TimeZone ID列表

    public static void main(String[] args) { Calendar c = new GregorianCalendar(); c.setTime(new Date()) ...

  8. vim出现“E212: Can't open file for writing”的处理办法

    在使用vim 对文件或配置进行编辑的时候,在保存时发现当前用户没有写权限.又不想放弃当前编辑的内容,怎么办呢? 来自stackoverflow “For some reason the file yo ...

  9. Angular之constructor和ngOnInit差异及适用场景

    constructor会在类生成实例时调用,Angular无法控制constructor,constructor中应该只进行依赖注入而不是进行真正的业务操作 ngOnInit属于Angular生命周期 ...

  10. django系列3.3--CBV 和 FBV

    一.CBV和FBV FBV function base views 用函数方法来处理请求 from django.http import HttpResponse def my_view(reques ...