Java泛型

Java 泛型的参数只可以代表类,不能代表个别对象。由于Java泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型,而且无法直接使用基本值类型作为泛型类型参数。Java编译程序在编译泛型时会自动加入类型转换的编码,故运行速度不会因为使用泛型而加快。Java允许对个别泛型的类型参数进行约束,包括以下两种形式(假设T是泛型的类型参数,C是一般类、泛类,或是泛型的类型参数)

泛型是 JDK1.5 引入的一种类型机制,就是将数据类型参数化,作为一种类型安全机制而产生的。 泛型机制就是将类型检查从运行时提前到了编译期,使用泛型编写的代码比杂乱的使用 Object,并在需要时再执行窄化处理的机制具备更好的可读性和安全性。 泛型在本质上就是进行类型的参数化。

泛型定义

public interface List<E> extends Collection<E> { //这里<>中的内容就是类型参数,一般建议使用全大写的方式进行定义,例 如 T、E、ID 之类的形式 
boolean add(E e); //定义 add 方法,要求传入的数据类型为 E 
E get(int index); //E 表示获取的数据类型就是定义接口时指定的类型

应用:获取两个整数中较大的整数

如果需要比较的不是 Integer 类型,而是 double 或者 float 类型,就需要再写 max 方法。引入泛型的目的就是在于定义 max 方法时,可以不确定参数 a 和参数 b 的数据类型,而是等到调用的时候再确定参数的具体数据类型,这样只需要定义一个 max 方法即可,从而降低编程的工作量。

 使用泛型的方式定义 max 方法:

public class MyTest { 
    public static <T extends Comparable<T>> T max(T t1, T t2){ //静态方法中直接使用泛型的语法为<T> T,定义泛型时 T extends Comparable,表示传入的类型必须实现了 Comparable 接口,否则编译错误 
        return t1.compareTo(t2)>0?t1:t2; //之所以可以直接调用 compareTo 方法是因为在类型声明时已经要求必须实现 Comparable 接口
    } 
}

泛型的好处:

解决类型安全性的隐患,泛型类或者接口在取出对象时不需要再进行向下类型转换,因为可以认为存储的时候就是这种类型。泛型的使用让安全问题再编译时就报错,而不是运行时报错,这样方便及时准确的发现问题。

1.可读性,从字面上就可以判断集合中的内容类型

2.类型检查,避免插入非法类型的数据

3.获取数据时不再需要强制类型转换

泛型类

泛型类也叫做参数化类型,就是具有一个或者多个类型参数的类,一个泛型类可以有多个泛型声明,所有的泛型声明都应该在<>内部。

在当前类中 T 就是一个类型的说明,可以用在说明任何实例方法中的局部变量、方法的形参以及方法的返回值,类的成员变量;但是类型 T 不能直接使用在静态方法中。

public class Generic<T>{ //<>中包含的全大写的名称就是泛型:形式参数 
    private T name; //在类中就可以使用使用泛型名称当作具体类型使用 
    public T getName(){ //方法可以直接使用泛型 
        return name; 
    }
    public void setName(T name){ 
        this.name=name; 
    } 
}

带多个类型参数的泛型类

如果引用多个类型,可以使用逗号作为分隔符,例如<S,D>

类型参数名称可以使用任意字符串,但是一般建议使用有代表含义的单个字符,以便于和普通类型名称进行区分。 例如 T 代表 type,源数据 S,目标数据 D,子元素类型 E

集合类中泛型

List<String> list=new ArrayList<>(); 
Map<String,Object> map=......; 
List<Map<String,Object>> list=....

泛型只能使用引用类型,而不能使用基本类型,例如 List<int>是错误。

有界类型

在实际应用中可能需要对传递的类型参数的具体类型进行限制。

public class MyClass<T extends Number> {} //定义的泛型类要求传入的具体类型必须是 Number 的子类,也就是要求传入的具体类型必须是数值型。这里可以传入 Integer、Double 等类型,但是不能传入 String

java 中提供了有界类型,在指定一个类型参数时,可以指定一个上界,声明所有的实际类型都必须时这个超类的直接或者间接子类。

Comparable 接口

java 预定义的接口

public interface Comparable<T>{
    public int compareTo(T o);
}

这里使用了泛型<T>,表示用于规范类的可比较性.compareTo(T o)当前类型对象比较时需要返回一个 int 整数,当当前对象大于参数 o 时返回一个正数,小于时返回一个负整数,等于返回为 0,进行比较的参数 o 必须是 T 类型。如果一个类实现了 Comparable 接口,则表示当前类型的对象是可比较大小的,也就是说可以进行排序。实现了 Comparable 接口的类支持排序,也就是可以使用工具类 Collections 对集合对象进行排序 ,Comparable 接口中定义一个比较方法 compareTo(T obj),返回的 int 类型数据用于表示大小。

public class Pig implements Comparable<Pig> { 
    private Long id; 
    private double weight; 
    public int compareTo(Pig pig){ 
        double res=this.weight-pig.weight; 
    if(Math.abs(res)<1e-6) 
        return 0; 
    else if(res>0) 
        return 1; 
    else 
        return -1;
    } 
}

使用:

List<Pig> list=new ArrayList<>(); 
Random r=new Random(); 
for(int i=0;i<10;i++){ 
    Pig tmp=new Pig(1L+i, r.nextDouble()*480+20); 
    list.add(tmp); 
}
Collections.sort(list); 
for(Pig p:list) 
    System.out.println(p);

Comparator接口

Comparator 是比较器接口。如果类本身不支持排序比较,即实现 Comparable 接口,则可以建一个类型的比较器专门用于排序比较。

例如 Pig 类没有实现 Comparable 接口,则使用 Collections.sort(list)则会报错。报错的原因是 Collection 工具类中的方法定义。

Comparable Comparator 接口比较

Comparable 接口是排序接口,如果一个类实现了 Comparable 接口就意味着该类型的对象是可比较的,而Comparator 接口是比较器,如果需要控制某个类的次序,可以临时建议一个该类的比较器进行排序,可以将 Comparable 当作内部比较器,而 Comparator 相当于外部比较器。

通配符参数

通配符参数一般用于方法中接收参数类型的定义

void doSomething(Status<?> ob)表示传入参数是任意的 Status 类型,其中?表示一个不确定的类型,它的具体值会在调用时才能确定下来。

public class Test{ 
    public void pp(List<?> list){ 传入的具体实参可以时 List 的任意类型 
        list.add(123); //语法报错 
    } 
}

基础语法:

//<? extends E>上限通配符,用以类型的上限
public class B1<T>{ 
    public void pp(List<? extends T> list){} 表示list中的元素类型必须是T类型或者T类型的后代类型 
}
//<? super T>下限统配符,用于限制类型的下限 
public class B1<T>{ 
    public void pp(List<? super T> list){} 表示list中的元素类型必须是T类型或者T类型的祖先类型 
}

泛型方法:

1.泛型类中方法

public class MyClass<T,ID>{ 
    public T pp(ID t){} 
}

2.没有泛型声明的普通类中

public class MyClass{ 
    public <T,ID> void show(T t,ID id){} 
}

3.静态方法的泛型

//静态方法无法使用类上的泛型声明 
public static <T,ID> T load(ID id){}

泛型的擦除

实际上从 VM 的角度上来说是不存在泛型概念。泛型是运用在编译期的技术:在编译时编译器会按照泛型声明对容器中的元素进行检查,检查不匹配则编译失败;如果全部检查成功则编译通过。但是编译通过后生成的.class字节码文件中并没有泛型声明的标识,也就是文件中并不会出现泛型,这就是泛型的擦除。

在.java 文件中运行泛型技术时,编译器在文件编译通过后自动擦除泛型标识

泛型的局限性

1、不能使用基本数据类型,应该使用对应类型的包装类

2、不能使用泛型类异常

3、不能使用泛型数组。声明泛型数组可以的,但是实例化就是问题

4、不能实例化参数类型对象 T obj=new T();。如果需要实例化则需要通过反射机制实现

对象克隆:

Object 类中定义 clone 方法可以实现对象的浅克隆,使用克隆的方式创建对象的成本远远小于 new 操作。【原型模式】

如果一个类需要支持浅克隆操作,则需要实现 Cloneable 接口【标志接口】,用于告知 VM 这个类型的对象需要支持克隆操作。如果一个类没有实现接口,当调用 clone()方法时则会抛出异常 CloneNotSupportException。

public class B implements Cloneable { 
    private Long id; 
    private String name; 
    ... 
    public Object clone() throws CloneNotSupportException{ //定义的目的在于其它位置调用,Object 类中的 clone 方法 是 protectedreturn 
        super.clone(); 
    } 
}

调用 clone 方法进行浅克隆操作:

B b1=new B(); //设置对应属性 b1.setId(100L); 
B b2=(B)b1.clone(); //b2 对象不同通过 new 创建的,而是克隆 b1 得到的对象 
System.out.println(b2==b1);//false,因为 b1

如果需要深克隆可以使用对象流实现,它会将所有相关的内容进行一次拷贝,而不会针对引用类型属性只是拷贝地址值。通过对象流实现对象的拷贝,则要求对象所属于的类必须实现 Serializable 接口。

public class A implements Serializable{}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值