Java泛型

Java泛型(Generics)

一个链接:Java泛型详解
另一个链接:Java知识点总结(Java泛型)

语法糖:
语法糖( Syntactic Sugar) , 也称糖衣语法, 是由英国计算机学家Peter.J.Landin发明的一个术语, 指在计算机语言中添加的某种语法, 这种语法对语言的功能并没有影响, 但是更方便程序员使用。 Java中最常用的语法糖主要有泛型、 变长参数、 条件编译、 自动拆装箱、 内部类等。 虚拟机并不支持这些语法, 它们在编译阶段就被还原回了简单的基础语法结构, 这个过程成为解语法糖。

目的

Java 泛型就是一种语法糖, 通过泛型使得在编译阶段完成一些类型转换的工作, 避免在运行时强制类型转换而出现 ClassCastException , 即类型转换异常。

优点

  1. 类型安全。 类型错误现在在编译期间就被捕获到了, 而不是在运行时当作java.lang.ClassCastException展示出来, 将类型检查从运行时挪到编译时有助于开发者更容易找到错误, 并提高程序的可靠性。
  2. 消除了代码中许多的强制类型转换, 增强了代码的可读性。
    引入泛型前后:
public static void main(String[] args)
{
    List list = new ArrayList();
    list.add("123");
    list.add("456");
    System.out.println((String)list.get(0)); // 引入泛型前,需要强制类型转换。如果之前add了一个不兼容类型的元素,就会出现异常。
}

public static void main(String[] args)
{
    List<String> list = new ArrayList<String>();
    list.add("123");
    list.add("456");
    System.out.println(list.get(0)); // 引入泛型后,在编译阶段就会有类型检查,运行时不需要进行强制类型转换。
}
  1. 为较大的优化带来了可能。

使用

1. 泛型类和泛型接口

允许在定义接口、 类时声明类型形参, 类型形参在整个接口、类体内可当成类型使用, 几乎所有可使用普通类型的地方都可以使用这种类型形参。
截图

泛型类派生子类
当创建了带泛型声明的接口、 父类之后, 可以为该接口创建实现类, 或者从该父类派生子类。
需要注意: 使用这些接口、 父类派生子类时不能再包含类型形参, 需要传入具体的类型。

public class A extends Container<K, V>{} // 错误
public class A extends Container<Integer, String>{} // 正确
public class A extends Container{} // 也可以不指定,此时系统会把K V类型形参当成Object类型处理

2. 泛型方法

所谓泛型方法, 就是在声明方法时定义一个或多个类型形参。

修饰符<T, S> 返回值类型 方法名(形参列表) {
方法体
}
class Demo{
    public <T> T fun(T t){ // 可以接收任意类型的数据
    return t ; // 直接把参数返回
    }
};
public class GenericsDemo26{
    public static void main(String args[]){
        Demo d = new Demo() ; // 实例化Demo对象
        String str = d.fun("汤姆") ; // 传递字符串
        int i = d.fun(30) ; // 传递数字, 自动装箱
        System.out.println(str) ; // 输出内容
        System.out.println(i) ; // 输出内容
    }
};

当调用 fun() 方法时, 根据传入的实际对象, 编译器就会判断出类型形参T所代表的实际类型。
方法声明中定义的形参只能在该方法里使用, 而接口、 类声明中定义的类型形参则可以在整个接口、 类中使用。

3. 泛型构造器

Java也允许在构造器签名中声明类型形参, 这样就产生了所谓的泛型构造器。
和使用普通泛型方法一样没区别, 一种是显式指定泛型参数, 另一种是隐式推断,如果是显式指定则以显式指定的类型参数为准, 如果传入的参数的类型和指定的类型实参不符, 将会编译报错。

public class Person {
    public <T> Person(T t) {
        System.out.println(t);
    }
}

public static void main(String[] args){
    //隐式
    new Person(22);
    //显示
    new<String> Person("hello");
}

类型通配符

类型通配符是一个问号(?), 将一个问号作为类型实参传给List集合, 写作: List<?> ( 意思是元素类型未知的List) 。 这个问号(?)被称为通配符, 它的元素类型可以匹配任何类型。

public void test(List<?> c){
    for(int i =0;i<c.size();i++){
        System.out.println(c.get(i));
    }
}

List<?> list = new ArrayList<String>();
test(list);
List<?> list1 = new ArrayList<Integer>();
test(list1);

list.add(new Object()); // 编译报错,因为程序无法确定c集合中元素的类型, 所以不能向其添加对象。

带限通配符

使用通配符的目的是来限制泛型的类型参数的类型, 使其满足某种条件, 固定为某些类。

1. 上限通配符

List<? extends Shape> list = new ArrayList<Circle>(); // 它表示集合中的所有元素都是Shape类型或者其子类

2. 下限通配符

List<? super Circle> list = new ArrayList<Shape>(); // 它表示集合中的所有元素都是Circle类型或者其父类

类型擦除

Class c1=new ArrayList<Integer>().getClass();
Class c2=new ArrayList<String>().getClass();
System.out.println(c1==c2); // true

这是因为不管为泛型的类型形参传入哪一种类型实参, 对于Java来说, 它们依然被当成同一类处理, 在内存中也只占用一块内存空间。 从Java泛型这一概念提出的目的来看, 其只是作用于代码编译阶段, 在编译过程中, 对于正确检验泛型结果后,会将泛型的相关信息擦除, 也就是说, 成功编译过后的class文件中是不包含任何泛型信息的。 泛型信息不会进入到运行时阶段。
在静态方法、 静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。由于系统中并不会真正生成泛型类, 所以instanceof运算符后不能使用泛型类。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值