泛型是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型,泛型也可以看做是一个变量,用来接收数据类型。
E e:Element元素
T t:Type 类型
众所周知,凡是都有两面性,首先我们来谈谈不使用泛型的好处与坏处。
创建集合对象,不使用泛型
好处:
集合不使用泛型,默认的类型是Object类型,可以存储任意类型数据。
坏处:
不安全,会引发异常。
举例说明
private static void show01() {
ArrayList list = new ArrayList();
list.add("abc");
list.add(3);
//使用迭代器遍历list集合
//获取迭代器
Iterator it = list.iterator();
//使用迭代器中的方法hasNext和next遍历集合
while (it.hasNext()){
//存的时候是Object类型,取出的时候也是Object
Object obj = it.next();
System.out.println(obj);
} //目前没问题
//想要使用String类特有的方法,length获取字符串长度;不能使用 多态 Object obj = "abc";
// 不能使用子类特有的方法 需要向下转型
// String s = (String)obj; //数字不能转换字符串 引发异常
// System.out.println(s.length());
}
那么我们接下来在来谈谈使用泛型的好处与坏处。
创建集合对象,使用泛型
好处:
1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
2.把运行期异常(代码运行之后会抛出的异常),提升到了编译器(写代码的时候会报错)
弊端:
泛型是什么类型,只能存储什么类型的数据
举例说明
private static void show02() {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
//使用迭代器遍历list集合
Iterator<String> it = list.iterator();
while (it.hasNext()){
String next = it.next();
System.out.println(next+"->"+next.length()); //abc->3
}
}
定义一个含有泛型的类,模拟ArrayList集合
泛型是一个未知的数据类型,当我们不确定什么什么数据类型的时候,可以使用泛型泛型可以接收任意的数据类型,可以使用Integer,String,Student…创建对象的时候确定泛型的数据类型
好处 :类型不写死,创建对象泛型是什么类型,类中泛型就是什么类型
首先定义一个含有泛型的类
public class 含有泛型的类 <E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
//所有使用数据类型的时候,都使用了泛型E
}
接着就可以在主方法中使用它了
public class 主方法 {
public static void main(String[] args) {
//不写泛型默认Object类型
含有泛型的类<Object> aa = new 含有泛型的类<>();
aa.setName("字符串");
Object name = aa.getName();
System.out.println(name);
// 创建含有泛型的类对象,泛型使用Intergr
// 创建对象的时候,确定泛型类型
含有泛型的类<Integer> aa1 = new 含有泛型的类();
aa1.setName(21);
Integer name1 = aa1.getName();
System.out.println(name1);
// 创建含有泛型的类对象,泛型使用String
含有泛型的类<String> aa2 = new 含有泛型的类();
aa2.setName("字符串");
String name2 = aa2.getName();
System.out.println(name2);
}
}
定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
格式:
修饰符<泛型> 返回值类型 方法名(参数列表(使用泛型)){方法体;}
含有泛型的方法,在调用方法的时候确定泛型的数据类型,传递什么类型的参数,泛型就是什么类型。
举例说明
定义含有泛型的方法
public class 泛型方法 {
//定义一个含有泛型的方法
// M那个写什么都行
public <M> void method01(M m){
System.out.println(m);
}
//定义一个含有泛型的静态方法
public static <S> void method02(S s){
System.out.println(s);
}
}
使用含有泛型的方法
public class 主方法 {
public static void main(String[] args) {
// 创建泛型方法对象
泛型方法 aa = new 泛型方法();
/*
调用含有泛型的方法method01
传递什么类型,泛型就是什么类型
*/
aa.method01(1);
aa.method01("爱睡觉");
aa.method01(3.3);
aa.method01(true);
aa.method02("静态方法,不建议创建对象使用");
// 静态方法,通过类名.方法名(参数) 可以直接使用
泛型方法.method02(33); //它也可以传递任意数据类型
}
}
含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型。例如:
Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法默认就是String
public final class Scanner implements Iterator<String>{
public String next(){}
}
含有泛型接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,实现类跟着接口走,就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型。
那么接下来我们就举例演示一下
首先定义含有泛型的接口
public interface 接口 <I>{
public abstract void method(I i);
}
第一种使用方式
public class 实现类1 implements 接口<String>{ //实现类指定泛型类型
@Override
public void method(String s) {
System.out.println(s);
}
}
第二种使用方式
public class 实现类2<I> implements 接口<I>{ //接口使用什么泛型 实现类就使用什么泛型
@Override
public void method(I i) {
System.out.println(i);
}
}
测试含有泛型的接口
public class 主方法 {
public static void main(String[] args) {
//创建实现类1对象
实现类1 aa =new 实现类1();
aa.method("字符串");
//创建实现类2对象 创建对象的时候,指定什么泛型 就是什么泛型
实现类2<Integer> bb =new 实现类2();
bb.method(22);
实现类2<Double> cc =new 实现类2();
cc.method(2.2);
// 跟定义含有泛型的类是一样的
}
}
public class 泛型的通配符 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
ArrayList<String> list2 = new ArrayList<>();
list2.add("a");
list2.add("b");
aa(list1);
aa(list2);
// ArrayList list3 = new ArrayList<>(?); 定义的时候不能用通配符,作为参数传递的时候可以用
}
/*
第一一个方法,能遍历所有类型的ArrayList集合
这时候我们不知道ArrayList集合使用什么数据类型,可以使用泛型通配符“?” 来接收数据类型
注意:
泛型没有继承概念的 里面不能写Object
*/
public static void aa(ArrayList<?> list){ // ArrayList list作为参数传递,使用通配符
//使用迭代器 遍历集合
Iterator<?> it = list.iterator();
while (it.hasNext()){
Object obj = it.next(); // 取出类型是Object类型,只要Object才能接收任意类型
System.out.println(obj);
}
}
}
泛型的上限限定:? extends E 代表使用的泛型只能是E类型的子类/本身
泛型的下限定:? super E 代表使用的泛型只能是E类型的父类/本身
按照斗地主的规则,完成洗牌发牌动作。
具体规则: 使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸排,每人17张牌,最后留三张作底牌
代码实战
public class 斗地主案例 {
public static void main(String[] args) {
//1. 准备54张牌,存储到一个集合中
//创建集合
ArrayList<String> list = new ArrayList();
// 定义一个数组,用来存储花色
String [] aa = {"♠","♥","♣","♦"};
// 定义一个数组,用来存储序号
String [] bb = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
//把大小王加入到集合中
list.add("大王");
list.add("小王");
// 循环嵌套遍历两个数组,组装52张牌
for (String s : aa) {
for (String s1 : bb) {
// System.out.println(s+s1);
list.add(s+s1);
}
}
// System.out.println(list);
//2. 洗牌
// 使用集合工具类Collections的方法
//static void shuffle(List list) 使用指定的随机源对指定列表进行置换,
// 会随机打乱集合中的元素顺序
Collections.shuffle(list);
//3.发牌
//定义四个集合存储三个玩家的牌和底牌
ArrayList<String> aa1 = new ArrayList<>(); //玩家1
ArrayList<String> aa2 = new ArrayList<>();//玩家2
ArrayList<String> aa3 = new ArrayList<>();//玩家3
ArrayList<String> aa4 = new ArrayList<>();//底牌
//
for (int i = 0; i < list.size(); i++) {
//获取每一张牌
String s = list.get(i);
//轮流发牌
if (i>=51){
//给底牌发牌
aa4.add(s);
}else if (i%3==0){
//给玩家1发牌
aa1.add(s);
}else if (i%3==1){
//给玩家2发牌
aa2.add(s);
}else if (i%3==2){
//给玩家3发牌
aa3.add(s);
}
}
// 看牌
System.out.println("张三"+aa1);
System.out.println("李四"+aa2);
System.out.println("麻子"+aa3);
System.out.println("底牌"+aa4);
}
}