泛型

泛型

引入

为什么需要学习泛型?,来看一下这个例子:

Collection c = new ArrayList();
c.add("张三");
c.add("李四");
c.add("王五");
c.add(20);
c.add(2.5);

// 遍历集合
for (Iterator it = c.iterator(); it.hasNext(); ) {
    Object oj = it.next();
    // 访问子类每一个元素所特有的方法
    String s = (String) oj;
    System.out.println("字符串的长度" + s.length());
}

程序出现如下异常:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

针对上述,可以使用 instanceof 关键字做向下转型,修改代码如下

for (Iterator it = c.iterator(); it.hasNext(); ) {
    Object oj = it.next();
    if (oj instanceof String) {
        // 访问子类每一个元素所特有的方法
        String s = (String) oj;
        System.out.println("字符串的长度" + s.length());
    } else if (oj instanceof Integer) {
        Integer i = (Integer) oj;
        System.out.println("整数的值: " + i.intValue());
    } else if (oj instanceof Double) {
        Double d = (Double) oj;
        System.out.println("小数的值: " + d.doubleValue());
    }
}

我们发现上述代码出现大量if-else语句,导致程序的可读性下降,并且程序的扩展性也很弱,如果容器需要添加一个自定义学生类。

c.add(new Student("1001", "张三", 30));

上述代码无法完全显示所有的集合内容:

1556346735234

我们可以继续添加 else if语句,但是我们知道Object的子类是无数的,所以每次针对Object的子类做逐一判断是不可能的,所以安全隐患永远存在,因此我们可以使用泛型改进。

泛型的由来:

1.泛型模仿了数组: 在编译时期就确定类型,不存在对所有的子类判断的问题

2.泛型模仿了方法:泛型是一种类似于方法的参数化类型

概念

泛型是JDK1.5之后引入的新特性,是一种将元素的类型提前在编译时期确定,并且它是一种参数化类型的技术

格式

  1. <>里面可以是任意的字母,一般泛型类会使用E,泛型方法会使用T
  2. 这里只能够定义引用类型,不能够定义基本书类型
  3. <>里面既可以定义一个泛型,也可以定义多个泛型

通过使用泛型改进代码,代码显示如下:

Collection<String> c = new ArrayList<String>();
		c.add("张三");
		c.add("李四");
		c.add("王五");
//		c.add(20);
//		c.add(2.5);
//		c.add(new Student("1001", "张三", 30));
Iterator<String> it = c.iterator();
while (it.hasNext()) {
    String s = it.next();
    System.out.println(s + "|" + s.length());
}

泛型的好处

  1. 简化了代码
  2. 取消了黄色警告线
  3. 取消了强制类型转换,提高了程序的效率
  4. 提高了程序的安全性
  5. 提高了程序的扩展性和可维护性,满足了开闭原则【对扩展开放,对修改关闭】

泛型类

泛型类: 把泛型定义在类上。

JDK1.5之前没有使用泛型的时候,代码如下:

public class GernericDemo02 {
	public static void main(String[] args) {
		GenericClass gc = new GenericClass();
		gc.setObj("张三");
		
		Object oj = gc.getObj();
		String s = (String) oj;
		System.out.println(s + "|" + s.length());
	}
}

class GenericClass {
	private Object obj;

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}
	
}

以上代码存在安全隐患,如果添加如下代码:

gc.setObj(10);

会出现类型转换异常,我们可以使用泛型类来改进,继续看如下代码:

泛型类改进:
public class GernericDemo02 {
	public static void main(String[] args) {
		GenericClass<String> gc = new GenericClass<String>();
		gc.setE("张三");
//		gc.setE(10);
		
		String s = gc.getE();
		System.out.println(s + "|" + s.length());
	}
}

class GenericClass<E> {
	private E e;

	public E getE() {
		return e;
	}

	public void setE(E e) {
		this.e = e;
	}
}

泛型接口

概念:把泛型定义在接口

泛型接口代码如下:

interface GenericInterface<E, T> {
	
	void test(E e);
	
	T add(T t);
}

泛型接口的使用方式有如下三种:

  1. 实现类确定泛型类型

    // 1.实现类确定泛型类型
    class GenericInterfaceImpl implements GenericInterface<String, Integer> {
    
    	@Override
    	public void test(String e) {
    		System.out.println(e);
    	}
    
    	@Override
    	public Integer add(Integer t) {
    		return t;
    	}
    	
    }
    
  2. 实现类不确定泛型,在调用的时候确定泛型

    // 实现类不确定泛型
    class GenericInterfaceImpl<E,T> implements GenericInterface<E, T> {
    
    	@Override
    	public void test(E e) {
    		System.out.println(e);
    	}
    
    	@Override
    	public T add(T t) {
    		return t;
    	}
    	
    }
    
    // 在调用的时候确定泛型
    GenericInterface<String, Double> gi = new GenericInterfaceImpl<String, Double>();
    System.out.println(gi.add(2.5));
    gi.test("hello");
    
  3. 匿名内部类确定泛型类型

    GenericInterface<String, Boolean> gi = new GenericInterface<String, Boolean>(){
    
        @Override
        public void test(String e) {
            System.out.println(e);
        }
    
        @Override
        public Boolean add(Boolean t) {
            return t;
        }
    
    };
    

泛型方法

概念: 把泛型定义在方法上,泛型方法又可以理解为局部泛型。

泛型方法的特点:

1. 泛型方法独立于泛型类或者泛型接口
2. 泛型方法在方法调用的时候确定类型
3. 一个泛型接口或者泛型类中可以有多个泛型方法
4. 一个泛型方法也可以定义多个泛型

泛型方法示例代码如下:

public class GenericDemo04 {
	public static void main(String[] args) {
		GenericMethod<String, Integer> gm = new GenericMethod<String, Integer>();
        // 2.泛型方法在方法调用的时候确定类型
		gm.show(20.5);
		
		Character c = gm.test('c');
		System.out.println(c);
		
		gm.method(25, 2.5);
	}
}

class GenericMethod<E,H> {
	
	private E e;
	private H h;
	
	// 1.泛型方法独立于泛型类或者泛型接口
	public <T> void show(T t) {
		System.out.println(t);
	}
	// 3.一个泛型接口或者泛型类中可以有多个泛型方法
	public <K> K test(K K) {
		return K;
	}
	// 4.一个泛型方法也可以定义多个泛型
	public <V, U> void method(V v, U u) {
		System.out.println(v);
		System.out.println(u);
	}
	
	public E getE() {
		return e;
	}
	public void setE(E e) {
		this.e = e;
	}
	public H getH() {
		return h;
	}
	public void setH(H h) {
		this.h = h;
	}
	
}

泛型方法的应用:

大家知道集合Collection中有一个将集合转换成数组的方法,如下所示:

Object[] toArray();

该方法存在安全隐患,代码如下所示:

Collection<String> con = new ArrayList<String>();
con.add("周星驰");
con.add("成龙");
con.add("李连杰");

Object[] objs = con.toArray();
for (Object oj : objs) {
    Integer integer = (Integer) oj;
    System.out.println(integer.intValue());
}

但是其实该方法也存在着另外一个重载的方法,如下所示:

<T> T[] toArray(T[] a);

方法的设计者无法知道使用者会往集合中存储何种数据类型,所以将将确定类型的权限交给调用来确定类型,那么我们就可以考虑使用泛型方法,使用泛型方法改进后,代码如下所示:

Collection<String> con = new ArrayList<String>();
con.add("周星驰");
con.add("成龙");
con.add("李连杰");

// <T> T[] toArray(T[] a); 取消了强制类型转换和安全隐患
String[] strs = con.toArray(new String[] {});
for (String s : strs) {
    System.out.println(s);
}

注:泛型方法在我们写框架的时候应用非常广泛,所以我们有必要掌握它。

泛型限定符

概念: 用来限定泛型的符号

泛型限定符的常用格式:

1. ?: 表示泛型可以是任意类型
2. ? `extends` E:表示泛型可以是E或者E的子类 
3.  ? `super` E :表示泛型可以是E或者E的父类

代码用例如下:

public class GenericDemo05 {
	public static void main(String[] args) {
        // Object并不是代表任意类型
		Collection<Object> c1 = new ArrayList<Object>();
        // ?: 表示泛型可以是任意类型
		Collection<?> c2 = new ArrayList<Object>();
		Collection<?> c3 = new ArrayList<Fu>();
		Collection<?> c4 = new ArrayList<Daughter>();
		
        // ? `extends` E:表示泛型可以是E或者E的子类 
		Collection<? extends Fu> c5 = new ArrayList<Fu>();
		Collection<? extends Fu> c6 = new ArrayList<Daughter>();
		Collection<? extends Fu> c7 = new ArrayList<Son>();
		// Collection<? extends Fu> c8 = new ArrayList<Object>(); 编译报错
		
        //  ? `super` E :表示泛型可以是E或者E的父类
		Collection<? super Fu> c9 = new ArrayList<Fu>();
		// Collection<? super Fu> c10 = new ArrayList<Daughter>(); 编译报错
		// Collection<? super Fu> c11 = new ArrayList<Son>(); 编译报错
		Collection<? super Fu> c12 = new ArrayList<Object>();
	
	}
}

class Fu {}

class Son extends Fu {}

class Daughter extends Fu{}

泛型限定符的应用

大家应该还记得Collection中有一些方法的形参是带有泛型限定符的,例如:

boolean addAll(Collection<? extends E> c) 

在使用这个方法的时候可以采用如下几种形式:

Collection<Fu> c = new ArrayList<Fu>();
c.addAll(new ArrayList<Fu>());
c.addAll(new ArrayList<Son>());
c.addAll(new ArrayList<Daughter>());

泛型嵌套

泛型嵌套:泛型中可以包含泛型

有以下常见几种情况:

  1. Collection嵌套Collection集合
  2. Collection嵌套Map集合
  3. Map嵌套Collection集合

代码实例如下:

public class GenericDemo06 {
	public static void main(String[] args) {
		// 存储: 由内到外添加到集合中
		Collection<Student> class01 = new ArrayList<>();
		class01.add(new Student("1001", "张三", 30));
		class01.add(new Student("1002", "李四", 31));
		class01.add(new Student("1003", "王五", 32));
		
		Collection<Student> class02 = new ArrayList<>();
		class02.add(new Student("1001", "张三", 30));
		class02.add(new Student("1002", "李四", 31));
		class02.add(new Student("1003", "王五", 32));
		
		Collection<Student> class03 = new ArrayList<>();
		class03.add(new Student("1001", "张三", 30));
		class03.add(new Student("1002", "李四", 31));
		class03.add(new Student("1003", "王五", 32));
		
		Collection<Collection<Student>> school = new ArrayList<Collection<Student>>();
		school.add(class01);
		school.add(class02);
		school.add(class03);
		
		// 遍历: 由外到内
		int index = 1;
		for (Collection<Student> classList : school) {
			System.out.println(index + "班");
			for (Student s : classList) {
				System.out.println("\t" + s);
			}
			index ++;
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值