所谓泛型类generic class就是具有一个或多个类型参数的类
public class Test1 {
@SuppressWarnings("all")
public static void main(String[] args) {
// A1<Object> aa2=new A1<String>();
A1<String> aa1 = new A1<>();
aa1.name = "zhangsan";
// aa1.name=555; 可以在编译期进行类型检查
A1<Date> aa3 = new A1<>();
aa3.setName(new Date());
System.out.println(aa3.getName().getYear());
A1 aa4=new A1();
aa4.setName("yanjun");
aa4.setName(123456);
}
}
class A1<T> { // 在当前类的成员定义中,可以将T当作类型使用
T name;
public T getName() {
return this.name;
}
public void setName(T name) {
this.name = name;
}
}
如果定义了泛型类,但是引用时不声明泛型值,系统则识别泛型为Object类型
- A1 aa=new A1(); 不会报错,但是有警告信息,不建议这种用法
- aa.setName("abc"); 正确
- aa.setName(123); 正确
- name可以传入任意类型的数据,因为系统识别 name为Object类型
class A1中T是类型参数的名称。在创建一个对象时,这个类型名称用作传递给A1的实际类型的占位符。因此在A1中,每当需要类型参数时,就会用到T。注意T是被括在<>中的。每个被声明的类型参数,都要放在尖括号中。由于Generic使用了类型参数,所以它是一个泛型类,也被称为参数化类型。
在A1类中使用T定义了一个属性T name。由于T只是一个占位符,所以name的实际类型要由创建对象时的参数传递进来。
T事实是一个数据类型的说明,它可以用来说明任何实例方法中的局部变量、类的成员变量、方法的形式参数以及方法的返回值。但是类型参数T不能使用在静态方法中。
public class Generic<T> {
private static T name;//语法报错
public static T getName(){//语法报错
return name;
}
}
最后还需要注意:声明一个泛型实例时,传递给形参的实参必须是类类型,而不能使用int或char之类的简单类型。如果不传递类型,则系统默认类型为Object
带两个类型参数的泛型类
如果引用多个类型,可以使用逗号分隔:<S, D>
类型参数名可以使用任意字符串,建议使用有代表意义的单个字符,以便于和普通类型名区分,如:T代表type,有原数据和目的数据就用S/D,子元素类型用E等。当然,你也可以定义为XYZ,甚至xyZ。
泛型是JDK1.5的所有新特性中最难深入掌握的部分,没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中
- List list=new ArrayList();
- list.add("123");
- list.add(456);
- list.add(new Date());
因为系统识别的类型为Object
使用泛型集合,可以将一个集合中的元素限定为一个特定类型,这样集合中就只能存储同一类型的对象,这样更安全;并且当从集合中获取一个对象时,编译器也知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
- List<String>list=new ArrayList<>(); 第2个类型可以不写,JDK1.7引入的泛型推导
- list.add(“123”);正确
- list.add(456);错误,类型不合法
在JDK1.5之后,你还可以按原来的方式将各种不同类型的数据放到同一个集合中,但是编译时会报一个unChecked警告
可以使用注解避免警告信息
@SuppressWarnings("rawtypes")
A1 aa4=new A1();
去除所有的警告信息 @SuppressWarnings("all")
泛型中的类型参数严格说明集合中装载的数据类型是什么和可以加入什么类型的数据,
Collection<String>和Collectin<Object>是两个没有转换关系的参数化的类型
集合类中定义范型
List<String> list = new ArrayList<String>();
Map<String,Object> map = …
List<Map<String,Object>> list …
范型只能使用引用类型,而不能使用基本类型,即:`List<int>`是错误的
有界类型
public class MyClass<T extends Number>{ } 含义是要求传入T的类型必须是Number或者Number的子类型
在实际应用中可能会需要对传递给类型参数的类型加以限制。比如需要创建一个泛型类,它包含了一个求数组平均值的方法。这个数组的类型可以是整型、浮点型,但显然不能是字符串类型或是其他非数值类型。
Java提供了有界类型bounded types。在指定一个类型参数时,可以指定一个上界,声明所有的实际类型都必须是这个超类的直接或间接子类。class classname <T extends superclass>
要求传入的泛型类型必须是Number抽象类的子类
public class Generic<T extends Number>{
public double sum(T... params) {
double res=0;
for(int i=0;i<params.length;i++)
res+=params[i].doubleValue(); //这里是Number中定义的方法
return res;
}
}
这里的extends不表示继承,只是说明传入的类型必须是Number或者Number的子类型
调用方法:
Generic<Long> g=new Generic<>():
double sum=g.sum(1L,2L,3L,4L,5L,6L);
System.out.println(sum);
因为Long是Number的子类,但是换一种类型String则会报错
Generic<String> g=new Generic<>(); //报错的原因是String不是Number的子类型
另外调用方式
Generic<Number> g=new Generic<>();
double sum=g.sum(1,2,3,4,5); //写法正确,因为Integer(1)也是Number类型的
- 接口和类都可以用来做上界。class Stats<T extends Comparable> 。
- 这里需要注意:针对上界接口使用的关键字仍然是extends而非implements。一个类型参数
- 可以有多个限界,如class Stats<T extends Comparable & Serializable>。限界类型
- 用&分隔,因为逗号用来分隔类型参数。在多个限界中,可以有多个接口,但最多只能有一个类。如果用一个类作为限界,它必须是限界列表中的第一个。