美文网首页
记一次代码检查发现的趣事

记一次代码检查发现的趣事

作者: sollian | 来源:发表于2018-10-09 11:18 被阅读27次

Android Studio代码检查的问题

看下面一段代码:

public class Car2 implements Serializable {
}

public class Car3 {
}

public class JavaDemo2 {
    public static void main(String[] args) {
    }

    public void test(Serializable s) {
        if (s instanceof Car2) {
        } else if (s instanceof Car3) {
        }
    }
}

注意test方法的参数是Serializable类型,但Car3类并没有实现Serializable接口。此时可以编译并运行。

修改代码,给Car3类添加final修饰符

public final class Car3 {
}

此时再看test方法:

图1 编译错误

Android Studio提示不可转换的类型,编译失败。

刨根问底

对于这个现象,一开始没有深入分析,因为在我们的使用场景中,这就是个bug。所以以为是IDE的处理不够完善。然而仔细分析才发现,是自己图样图森破了。

Car3如果没有final修饰的话,完全可以构造一个Car4类:

public class Car4 extends Car3 implements Serializable {
}

Car4的实例传给test方法,就进入了else if分支。而当Car3final修饰符时,自然就不会有Car4这个子类了,所以会报编译错误。


类型擦除那点事

代码中的问题可以抽象成一个简单的例子。有两个类:TeacherStudent

    public static class Teacher {
        private final String name;

        public Teacher(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Teacher{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    public static class Student {
        private final String name;

        public Student(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

各有一个name变量,没什么好解释的。然后重点来了:

    public static void main(String[] args) {
        List<Teacher> teachers = new ArrayList<>();
        teachers.add(new Teacher("teacher1"));
        teachers.add(new Teacher("teacher2"));

        Object obj = teachers;

        List<Student> students = (List<Student>) obj;
        for (Object object : students) {
            System.out.println(object);
        }
    }

编译、运行都没有问题。输出如下:

Teacher{name='teacher1'}
Teacher{name='teacher2'}

哎呀,List<Student> students = (List<Student>) obj;竟然没报错,惊不惊喜,意不意外?不仅如此,for循环从students中取出的竟然是Teacher类!

这是一个很典型的类型擦除的示例,查看反编译的class文件就一目了然了:

    public static void main(String[] var0) {
        ArrayList var1 = new ArrayList();
        var1.add(new JavaDemo2.Teacher("teacher1"));
        var1.add(new JavaDemo2.Teacher("teacher2"));
        List var3 = (List)var1;
        Iterator var4 = var3.iterator();

        while(var4.hasNext()) {
            Object var5 = var4.next();
            System.out.println(var5);
        }
    }

可以看到,list的类型被擦除,难怪强转在运行时不会报错。而obj变量在编译期无法确定具体类型,自然在编译期也就不会报错了。

类型擦除算是Java的必修课,不过在实际coding中很少会注意到。通过这个问题,才发现原来有好多的思维定式需要改变。
IDE不报错,不代表程序就是合格的。

相关文章

网友评论

      本文标题:记一次代码检查发现的趣事

      本文链接:https://www.haomeiwen.com/subject/kaqeaftx.html