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> 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的子类
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,135评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,317评论 3 397
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,596评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,481评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,492评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,153评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,737评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,657评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,193评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,276评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,420评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,093评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,783评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,262评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,787评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,427评论 2 359

推荐阅读更多精彩内容

  • 在之前的文章中分析过了多态,可以知道多态本身是一种泛化机制,它通过基类或者接口来设计,使程序拥有一定的灵活性,但是...
    _小二_阅读 686评论 0 0
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,267评论 0 16
  • 开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收List作为形式参数,那么如果尝试...
    时待吾阅读 1,057评论 0 3
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,658评论 0 11
  • 因为气候的变化,医院里幼儿患者增多。赵子清的工作变得忙碌起来。面对看见针管就大哭的孩子,赵子清眼睛也不眨的把针扎了...
    少女唐辛子阅读 265评论 0 2