泛型-通配符

泛型

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

public interface List<E> extends Collection<E> { //这里<>中的内容就是类型参数,一般建议使用全大写的方式进行定义,例如 T、E、ID 之类的形式
boolean add(E e); //定义 add 方法,要求传入的数据类型为 E
E get(int index); //E 表示获取的数据类型就是定义接口时指定的类型调用
List<String> list=new ArrayList<String>(); //就是将 String 传递给 E,用于替代定义中的 E
String str=list.get(0);
//如果在调用时不进行声明,则系统默认为 Object
List<Date> list=new ArrayList<>(); //使用菱形语法,从 JDK1.7 开始支持泛型推导
list.add("123"); //编译时语法报错,因为编译期会进行类型检查,"123"不是 Date 类型
list.add(123); //语法报错
list.add(new Date()); //编译通过,类型合法

典型应用
需求:获取两个整数中较大的整数
Integer max(Integer a, Integer b){
return a>b?a:b;
}
如果需要比较的不是 Integer 类型,而是 double 或者 float 类型,就需要再写 max 方法。引入泛型的目的就是在于定义 max 方法时,可以不确定参数 a 和参数 b 的数据类型,而是等到调用的时候再确定参数的具体数据类型,这样只需要定义一个 max 方法即可,从而降低编程的工作量

Comparable 接口

该接口是用来实现对象的排序比较的一个接口,一般自定对象要实现按照一定规则进行排序(比如说要求按照升序或者是降序排列显示),那么可以让需要排序的对象的类去实现“Comparable”接口,然后覆写该接口的一个compareTol),排序的规则就是在该方法定义的,
这里使用了泛型,表示用于规范类的可比较性
compareTo(T o)当前类型对象比较时需要返回一个 int 整数,当当前对象大于参数 o 时返回一个正数,小于时返
回一个负整数,等于返回为 0,进行比较的参数 o 必须是 T 类型
例如要求整数类型是可比较的
在这里插入图片描述

对应的方法实现
在这里插入图片描述

调用另外的方法
在这里插入图片描述

使用泛型的方式定义 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 接口
}
}
//如果进行整型数据比较,需要先确定 Integer 必须实现了 Comparable 接口
Integer kk=MyTest.max(12,15);
//如果是 double 类型数据,要先确定 Double 必须实现了 Comparable 接口
Double dd=MyTest.max(12.12,13.)
public class Test3 {
public static void main(String[] args) {
Integer kk = MyTest.max(12, 15);
System.out.println(kk);
double dd = MyTest.max(12.34, 55.);
System.out.println(dd);
Person p1 = new Person(1234.56);
Person p2 = new Person(345.67);
Person pp = MyTest.max(p1, p2);
System.out.println(pp);
}
}
class Person implements Comparable<Person> {
private double salary;
public Person(double salary) {
super();
this.salary = salary;
}
@Override
public int compareTo(Person o) {
double res = this.salary - o.salary;
if (Math.abs(res) < 1e-6)
return 0;
else if (res > 0)
return 1;
else
return -1;
}
@Override
public String toString() {
return "Person [salary=" + salary + "]";
}
}

使用泛型的好处

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

  • 可读性,从字面上就可以判断集合中的内容类型
  • 类型检查,避免插入非法类型的数据
  • 获取数据时不再需要强制类型转换

泛型类

泛型类也叫做参数化类型,就是具有一个或者多个类型参数的类,一个泛型类可以有多个泛型声明,所有的泛型声明都应该在<>内部。
在当前类中 T 就是一个类型的说明,可以用在说明任何实例方法中的局部变量、方法的形参以及方法的返回值,
类的成员变量;但是类型 T 不能直接使用在静态方法中

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

使用

Generic<String> en=new Generic<>(); //使用时在具体确定 T 对应的类型为 String,第二处<>中没有内容,叫做泛型推导
en.setName("object"); //en.setName(123)语法报错,因为 123 不是 String 类型
System.out.println(en.getName());
//注意传递给泛型参数的类型必须时类类型,不能使用 int 或者 char 之类的简单类型
//如果定义了泛型类,但是引用时不声明泛型对应的类型值,则系统识别为 Object 类型
Generic en=new Generic();
en.setName("object"); //有警告信息,但是语法正确,因为 String 也是 Object 类型
en.setName(123); //有警告信息,但是编译可以通过。因为 123 经过自动装箱操作后,可以识别为 Object 类型

Comparable 和 Comparator 接口

Comparable 简介
Comparable 是排序接口。
若一个类实现了Comparable接口,就意味着“该类支持排序”。此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。
接口中通过x.compareTo(y)来比较x和y的大小。若返回负数,意味着x比y小;返回零,意味着x等于y;返回正数,意味着x大于y。

Comparator 简介
Comparator 是比较器接口。我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。

int compare(T o1, T o2)和上面的x.compareTo(y)类似,定义排序规则后返回正数,零和负数分别代表大于,等于和小于。

两者的联系
Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
Collections如何调用重写的compareTo方法的
集合框架中,Collections工具类支持两种排序方法:

Collections.sort(List list);
Collections.sort(List list, Comparator<? super T> c)
1
2
如果待排序的列表中是数字或者字符,可以直接使用Collections.sort(list);当需要排序的集合或数组不是单纯的数字型时,需要自己定义排序规则,实现一个Comparator比较器。

Collections调用Collections.sort(list)方法,方法传递一个List集合,这里要求,List泛型里面装的元素必须实现Compareable接口此外,列表中的所有元素都必须是可相互比较的(也就是说,对于列表中的任何 e1 和 e2 元素,e1.compareTo(e2) 不得抛出 ClassCastException)。Java源码里是这样写的
All elements in the list must implement the {@link Comparable}interface.Furthermore, all elements in the list must be <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must not throw a {@code ClassCastException} for any elements

通配符参数

1、通配符类型(?)
通配符类型可以理解为一种泛型调用时传递的一种特殊数据类型,表示参数允许在某个范围内变化。通配符类型有三种,分别是?,? extends,? super。
通配符表示一种未知类型,并且对这种未知类型存在约束关系.

<?>

?的默认是实现是? extends Object,表示?是继承Object的任意类型。

<? extends T> 上限通配

这里?表示一个未知的类,而T是一个具体的类,在实际使用的时候T需要替换成一个具体的类,表示实例化的时候泛型参数要是T或T的子类。

public static void function1(List<? extends Number> list){ 
    System.out.println(list.get(0).toString()); 
}
//使用方法
List<Integer> list1 = new ArrayList<>();
list.add(1);
List<Number> list2 = new ArrayList<>();
list2.add(2);
Demo.function1(list);
Demo.function1(list2);

<? super T> 下限通配



**
这里?表示一个未知的类,而T是一个具体的类,在实际使用的时候T需要替换成一个具体的类,表示实例化的时候泛型参数要是T或是T的父类。
例如:

//定义方法
public static void function2(List<? super Integer> list){ 
    System.out.println(list.get(0).toString()); 
}
//使用方法
List<Integer> list = new ArrayList<>();
list.add(1);
List<Number> list2 = new ArrayList<>();
list2.add(2);
Demo.function2(list);
Demo.function2(list2);

2、E、T、K、V、N

本质上以下字母都可以相互替换,但我们按照下面定义约定俗成的含义来使用。

E - Element (在集合中使用,因为集合中存放的是元素)

T - Type(Java 类)

K - Key(键)

V - Value(值)

N - Number(数值类型)

? - 表示不确定的java类型

S、U、V - 2nd、3rd、4th types

在定义泛型时,每个字母都限定代表同一个类型。比如使用T时,会限定多处使用T的指定类型必须要相同。
在下面例子中,src2和dest2的泛型类型必须相同,否则编译无法通过。
例如:

//定义T参数的方法
public static <T> void copy2(List<T> src, List<T> dest) {
    System.out.println("doSomething..");
}

//正确使用
List<Number> src2 = new ArrayList<>();
List<Number> dest2 = new ArrayList<>();
Demo.copy2(src2, dest2);
	
//错误使用
List<Number> src2 = new ArrayList<>();
List<Integer> dest2 = new ArrayList<>();
Demo.copy2(src2, dest2);

3、? 与 T 的差别

? 表示一个未知类型, T 是表示一个确定的类型。 因此,无法使用 ? 像 T 声明变量和使用变量。
例如:

// 正确定义
static <T> void test1(List<T> list) {
    T t = list.get(0);
    t.toString();
}
// 错误定义
static void test2(List<?> list){
    ? t = list.get(0);
    t.toString();
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值