第十五章、泛型
一、泛型的理解和好处
1、 问题
1、不能对加入到集合ArrayList中的数据类型进行约束(不安全)
2、遍历的时候,需要进行类型转换,如果集合中的数据大,会影响效率
2、好处
1、编译时,检查添加元素的类型,提高了安全性
2、减少了类型转换的次数(用到向下转型)提高效率
(1)eg:
//无泛型
ArrayList list = new ArrayList();
list.add(new Dog("大黄",2));
list.add(new Dog("二哈",3));
list.add(new Dog("小布",4));
for(Object obj:list) {
Dog d1 = (Dog)obj;//使用了类型转换
System.out.println(d1.getName());
}
//有泛型
ArrayList <Dog1> list = new ArrayList<Dog1>();
list.add(new Dog1("大黄",2));
list.add(new Dog1("二哈",3));
list.add(new Dog1("小布",4));
// list.add(new Cat("咪咪", 1));添加失败
for(Dog1 dog : list) {//省略了强制类型转换
System.out.println(dog.getName());
}
}
3、泛型介绍
1、泛型又称参数化类型,1.5版本后的,解决数据类型的安全性问题
2、在类声明或实例化时只要指定好的需要的具体的类型即可
3、java泛型会有提示,如果程序在编译类型没有发出经过,运行时就不会产生ClassCaseException类型转换异常,代码更简洁、强壮
4、作用:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值类型、或者是参数类型。
(1)eg:
public static void main(String[] args) {
// TODO Auto-generated method stub
US <String> u = new US("哈宝");
}
class US <E> {//类定义泛型
E ss;//属性定义泛型,E表示ss的数据类型,该数据类型在定义Persion对象时指定,在编译期间,就确定E的编译类型
public US(E ss) {//E也可以是参数类型
this.ss=ss;
}
public E ff() { //方法返回类型也可以定义
return ss;
}
}
4、泛型的语法
4.1、泛型的声明
1、interface 接口
2、说明
(1)其中:T,K,V 不代表值,而是表示类型。
(2)任意字母都可以。常用T表示,是Type的缩写
4.2、泛型的实例化
1、eg
public static void main(String[] args) {
// TODO Auto-generated method stub
Set <Student> set = new HashSet <Student> ();
set.add(new Student("傻瓜",11));
set.add(new Student("恶狗 ",16));
set.add(new Student("蕾姆",18));
for(Student student:set) {
System.out.println(student);
}
HashMap<String, Student> hashMap1 = new HashMap<String,Student>();
hashMap1.put("ton", new Student("憨包", 20));
hashMap1.put("jack", new Student("包包", 28));
hashMap1.put("smith", new Student("hsp", 35));
// entryset取出。迭代器
Set<Map.Entry<String, Student>> entries = hashMap1.entrySet();
Iterator<Map.Entry<String, Student>> iterator = entries.iterator();
while(iterator.hasNext()) {
Map.Entry<String, Student> next = iterator.next();
System.out.println(next.getKey()+"——"+next.getValue());
}
}
5、泛型使用的注意事项
1、interface List
(1)说明:T,E只能是引用类型,且可以是多个
(2)eg:int错误,int是基本数据类型
2、在指定泛型具体类型后,可以传入该类型或其子类类型
(1)理解:就是支持了继承
3、泛型的定义形式两种
(1)List
(2) List
(3)推荐(2)写法
4、如果我们这样写 List list3 = new ArrayList();
默认给它的泛型就是
6、自定义泛型
6.1、基本语法
1、class 类名<T,R…>{ //…代表可以多个
成员
}
6.2、注意细节
1、普通成员可以使用泛型(属性、方法)
2、使用泛型的数组,不能初始化
3、静态方法中不能使用类的泛型
4、泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
5、如果在创建对象时,没有指定类型,默认为Object
6、eg
class Tiger<T,R,M>{
String name;
R r;
M m;
T t;
T ts[];//可以定义,不能初始化
// T[] ts = new T[8] 参数类型不允许直接实例化。因为数组在new不能确定T的类型,无法在内存开辟空间
public Tiger(String name, R r, M m, T t) {
super();
this.name = name;
this.r = r;
this.m = m;
this.t = t;
}
// static R r2;
// 因为静态是和类相关的,类加载时,对象不加载。
// 所以静态方法和静态属性使用了泛型,JVM无法判断大小
7、自定义泛型接口
7.1、基本语法
interface 接口名<T,R…>{ //…代表可以多个 成员
7.2注意细节
1、接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
2、泛型接口的类型,在继承接口或者实现接口时确定
3、没有指定类型,默认为Object
7.3、eg
interface IA extends IUsb<String, Double>{
}
//当我们去实现IA接口时,因为IA在继承IUse 接口时,指定了U为String,R为Ddouble类型
class AA implements IA{
@Override
public Double get(String u) {
// TODO Auto-generated method stub
return null;
}
@Override
public void hi(Double r) {
// TODO Auto-generated method stub
}
@Override
public void run(Double r1, Double r2, String u1, String u2) {
// TODO Auto-generated method stub
}
}
class CC implements IUsb{ //如果没写,泛型默认为Object
@Override
public Object get(Object u) {
// TODO Auto-generated method stub
return null;
}
@Override
public void hi(Object r) {
// TODO Auto-generated method stub
}
@Override
public void run(Object r1, Object r2, Object u1, Object u2) {
// TODO Auto-generated method stub
}
}
interface IUsb<U,R>{
int n =10;
// U name; 不能这样使用
// 普通方法中,可以使用接口泛型
R get(U u);
void hi(R r);
void run(R r1,R r2,U u1,U u2);
// 在jdk8中,可以在接口中,使用默认方法,也是可以这样使用发那些
default R method(U u) {
return null;
}
}
8、自定义泛型方法
8.1基本语法
1、修饰符 <T,R…> 返回类型 方法名(参数列表){
}
8.2、注意细节
1、泛型方法,可以定义在普通类中,也可以定义在泛型类中
2、泛型方法被调用时,类型确认
3、public void eat(E e){ }。修饰符后没有<T,R…>eat 方法不是泛型类型,而是使用了泛型。
8.3、eg
Car car = new Car();
car.fly("宝马", 100);//当调用方法时,传入参数,编译器,就会确定类型
}
}
class Car{ //普通类
public void run() {//普通方法
}
// 说明
// 1、<T,R>就是泛型
// 2、是提供给fly使用的
public <T,R>void fly(T t,R r){//泛型方法
System.out.println(t.getClass());//String类型
System.out.println(r.getClass());//Integer类型
}
}
class Fish<T,R>{
public void run() {//普通方法
}
public <U,M> void eat() {
}
// 说明
// 1、下面hi方法不是泛型方法
// 2、是普通方法使用类定义的泛型
public void hi(T t) {
}
// 泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
public <K> void hello(R r,K k) {
}
}
9、泛型的继承和通配符
1、泛型不具备继承性
(1)List
2、<?>:支持任意泛型类型
3、<? extends A>:支持A类以及A类的子类,规定了泛型的上线
4、<? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限。
5、eg
// 泛型没有继承性
// List <Object> list = new ArrayList<>();
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<AA1> list3 = new ArrayList<>();
List<BB1> list4 = new ArrayList<>();
List<CC1> list5 = new ArrayList<>();
// 这种方法<?>,全部加的进去
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);
// 这种方法 List<? extends AA> c 表示 上限,可以接受AA或者AA子类
printCollection2(list1);//不行
printCollection2(list2);//不行
printCollection2(list3);//可以
printCollection2(list4);//可以
printCollection2(list5);//可以
// 这种方法 List<? super
printCollection3(list1);//可以
printCollection3(list2);//不行
printCollection3(list3);//可以
printCollection3(list4);//不行
printCollection3(list5);//不行
}
public static void printCollection1(List<?>c) { //通配符,取出时,就是Object
for(Object object : c ) {
System.out.println(object);
}
}
// ? extends AA 表示 上限,可以接受AA或者AA子类
public static void printCollection2(List<?extends AA1> c) { //通配符,取出时,就是Object
for(Object object : c ) {
System.out.println(object);
}
}
// ?super 子类类名AA1:支持AA类以及AA类的父类,不限直接父类。
public static void printCollection3(List<? super AA1>c) { //通配符,取出时,就是Object
for(Object object : c ) {
System.out.println(object);
}
}
}
class AA1{
}
class BB1 extends AA1{
}