Java 内部类和泛型的理解

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 getMiddle(T...a)
调用时可以省略类型参数,编译器可以自行推断

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

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

注意:

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

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

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

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

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

通配符类型

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

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

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

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

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

ListList 是不同的 ,List只能插入null

综合理解

//定义 Level3 extends Level2,Level2 extends Level1
    public static void main(String[] args) {
        List a = new ArrayList<>();
        List 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文档

Java 内部类和泛型的理解_第1张图片
Type的子类

你可能感兴趣的:(Java 内部类和泛型的理解)