美文网首页
Java 内部类和泛型的理解

Java 内部类和泛型的理解

作者: wangdy12 | 来源:发表于2017-12-30 17:12 被阅读0次

    Java 内部类

    Java非静态内部类持有对外部类的引用,可以访问外部类的状态
    使用方法 OuterClass.this.variable
    编译结果 OuterClass$InnerClass

    内部类访问修饰符可以为private,而普通类的修饰符不能是private,只可以具有包可见性或者公有可见性

    实例化一个公有内部类:

    Outer_Class outer = new Outer_Class();
    Outer_Class.Inner_Class inner = outer.new Inner_Class();
    
    1. 内部静态类不需要有指向外部类的引用;但非静态内部类需要持有对外部类的引用
    2. 非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员,只能访问外部类的静态成员。
    3. 一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。

    注意

    1. 内部类的静态域必须是final
    2. 内部类不能有static方法

    内部类似C++ 中的嵌套类,方便进行命名控制和访问控制

    局部内部类

    定义在方法内部,不能用private或public访问说明符修饰,作用域被限定到这个局部类的块中

    局部类不仅可以访问外部类,还可以访问final局部变量

    原理:
    局部内部类有一个实例域(this$0),是外部类的引用
    局部内部类对要访问的局部变量对应的备份
    final表明初始化后不能再修改数据,保证了局部变量与局部类内部建立的拷贝是一致的

    匿名内部类

    如果只创建这个类的一个对象,可以不必命名,称为匿名内部类(anonymous inner class)
    匿名内部类在声明的同时,进行实例化。用来重载类或者接口的方法

    new AnonymousInner(){
          inner class methods and data
    }
    

    因为匿名类没有类名,所以也没有构造器,若有需要应该把参数传递给超类构造器
    在构造参数的闭小括号后面跟一个开大括号,正在定义的就是匿名内部类

    Handler handler = new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
           //处理对应的消息
        }
    };
    

    静态内部类

    只是为了把一个类隐藏在另一个类的内部,不需要引用外围类的对象

    静态内部类可以有静态域和方法
    声明在接口中的内部类,自动成为public和static类


    接口

    接口中可以包含常量,不能包含实例域,
    可以包含静态方法,可以包含默认方法(用default修饰符标记)


    泛型

    泛型编程(generic programming)可以方便代码被不同的类型重用
    泛型的本质是参数化类型(Parametersized Type),操作的数据类型被指定为一个参数,这种类型参数可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法

    泛型方法的定义:类型变量放在修饰符的后面,返回类型的前面public <T> T getMiddle(T...a)
    调用时可以省略类型参数,编译器可以自行推断

    Java泛型实现是通过类型擦除实现的,类型参数被替换为原生类型(Raw Type)(或者说是限定类型),代码相应地方被插入类型强制转换,这实际是一种伪泛型
    C++的泛型是真实的,例如List<int>List<String>是两种不同的类型,编译生成不同的代码,成为这种泛型会导致类型膨胀

    由于类型擦除,可能会影响多态性,引入桥方法(编译器实现的方法,实现对泛型擦除后的方法的覆盖)

    注意:

    • 运行时进行泛型类型查询只产生原始类型 ,例如
    ArrayList<int> a ;
    ArrayList<String> b; 
    a.getClass()==b.getClass()//类型都是ArrayList.class
    
    • 支持可变长度的泛型类型的参数,使用@SafeVarargs抑制警告
    @SafeVarargs static <E> E[] array(E... array) {return array; }
    
    • 不能在静态字段或方法中引用类型变量
    private static T field; //错误
    
    • 不能创建泛型数组,数组会记住元素的类型,如果存入类型不正确应该抛出ArrayStoreException异常,然而类型擦除使得泛型的参数类型消失,在运行中无法进行监测

    但是允许创建通配符类型的数组

    // Not really allowed.
    List<String>[] lsa = new List<String>[10];
    // OK, array of unbounded wildcard type.
    List<?>[] lsa = new List<?>[10];
    

    声明参数化类型的数组是可行的,所以可以实现如下的泛型数组

    //HashMap中的泛型数组
    @SuppressWarnings({"rawtypes","unchecked"})
    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
    //ConcurrentHashMap中的泛型数组
    @SuppressWarnings("unchecked")
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    

    通配符类型

    通配符类型允许类型参数变化,通配符,分为子类型限定、超类型限定和无限定。通配符不是类型变量,因此不能在代码中使用"?"作为一种类型

    子类型限定
    表示类型的上界,格式? extends A
    作用:主要用来读取数据,可以访问A及其子类型

    超类型限定
    表示类型的下界,限定为A和A的超类型,格式是? super A特点:
    作用:主要用来写入数据,可以写入A及其子类型

    public static void addNumbers(List<? super Number> list) {
        for (int i = 1; i <= 10; i++) {
            list.add(i); //可以添加Integer以及它的子类
        }
    }
    

    举例:
    Collections类中的public static <T extends Comparable<? super T>> void sort(List<T> list)方法的类型声明,可以处理T没有实现Comparable接口,而T的父类实现了的情况,类似于外部提供比较器的方法:
    public static <T> void sort(List<T> list, Comparator<? super T> c)

    无限定通配符
    用法 ,例如List<?>,表示未知类型的list

    • 如果方法可以通过Object类提供的函数实现功能
    • 使用泛型类的方法,不依赖类型参数的情况,例如List.size List.clear

    使用场景:

    public static void printList(List<?> list) {
        for (Object elem: list)
            System.out.print(elem + " ");
        System.out.println();
    }
    

    List<Object>List<?> 是不同的 ,List<?>只能插入null

    综合理解

    //定义 Level3 extends Level2,Level2 extends Level1
        public static void main(String[] args) {
            List<? extends Level2> a = new ArrayList<>();
            List<? super Level2> b = new ArrayList<>();
    //        a.add(new Level1(0));
    //        a.add(new Level2(0));    // 错误
    //        a.add(new Level3(0));
            a.add(null); // works
    
            Level1 a1 = a.get(0);
            Level2 a2 = a.get(0);
    //        Level3 a3 = a.get(0); //错误
    
    
    
    //        b.add(new Level1(0));     //错误
            b.add(new Level2(0));
            b.add(new Level3(0));
    
            Object o = b.get(0);
    //        Level1 b1 = b.get(0);
    //        Level2 b2 = b.get(0);   // 错误
        }
    

    虚拟机中的泛型类型信息

    Class文件实际保留了泛型信息,位于在Signature属性中,类型擦除只是对Code属性中的字节码
    可以通过反射读取相关信息,其核心是Type类型,根据其不同的子类型,获取泛型信息,具体方法可以查看API文档

    Type的子类

    相关文章

      网友评论

          本文标题:Java 内部类和泛型的理解

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