泛型以及Set系列集合
一、泛型深入
1.泛型:
是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
2.泛型的格式
< 数据类型 >
3.注意
★泛型只支持引用数据类型,没有泛型约束的时候,可以往集合中添加任意类型的数据
★但是遍历集合得到的数据聚类形是Object类型的,而多态的弊端是不能访问子类的特有功能
★虽然可以使用强制转换,但是并不能明确知道强转的类型
4.结论:
★如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的
★此时可以往集合中太假任意的数据类型
★带来一个坏处,我们在获取数据的时候,无法使用它的特有行为
★此时推出了泛型,可以在添加数据的时候,就把类型进行统一
★而且我们在获取数据的升级后,也省的强转了,比较的方便
package com_06Gather._01gatherStructure._02SetTest;
import com_06Gather._01gatherStructure.Student;
import java.util.ArrayList;
import java.util.Iterator;
public class GenericsTest01 {
public static void main(String[] args) {
/*没有泛型的时候,集合如何存储数据
结论
★如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的
★此时可以往集合中太假任意的数据类型
★带来一个坏处,我们在获取数据的时候,无法使用它的特有行为
★此时推出了泛型,可以在添加数据的时候,就把类型进行统一
★而且我们在获取数据的升级后,也省的强转了,比较的方便
*/
//1.创建集合的对象
ArrayList list = new ArrayList();
//2.添加数据
list.add(123); //Integer
list.add("abc"); //String
list.add(new Student("zhangsan",12)); //Student
//3.遍历集合获取里面的每一个元素
Iterator it = list.iterator();
while(it.hasNext()){
Object obj = it.next();
//堕胎的弊端是 不能访问子类的特有方法
System.out.println(obj);
}
}
}
5.泛型的好处
★统一数据类型
★把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来
6.扩展知识点
★Java中的泛型其实是伪泛型:
7.泛型的细节
★泛型中不能写基本数据类型
★指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类型
★如果不写泛型,类型默认是Object
8.泛型可以在很多地方进行定义
(1)类后面===》泛型类
①使用场景:
当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
②格式:
修饰符 class 类名 < 类型 > {
}
//例如:
public class ArrayList<E>{ //这里的E代表不确定的类型,但是创建该类对象时,E就确定类型
//此处E可以理解为变量,但不是用来记录数据的,而是用来记录类型的,可以写成:T\K\E\V等
}
package com_06Gather._01gatherStructure._02SetTest;
public class GenericsTest03_MyArrayListTest02 {
public static void main(String[] args) {
GenericsTest02_MyArrayList<String> list = new GenericsTest02_MyArrayList<>();
list.add("wewew");
String s = list.get(0);
System.out.println(list);
System.out.println(s);
GenericsTest02_MyArrayList<Integer> list2 = new GenericsTest02_MyArrayList<>();
list2.add(123);
list2.add(456);
list2.add(789);
Integer i = list2.get(2);
System.out.println(i);
System.out.println(list2);
}
}
(2)方法上面===》泛型方法
①使用场景
当方法中形参类型不确定时,可以使用类名后面定义的泛型< E >
方案一:使用类名后面定义的泛型===》所有方法都能使用
方案二:在方法申明上定义自己的泛型===》只能本方法使用
//格式
修饰符 < 类型 > 返回值类型 方法名 (类型 变量名){
}
//例如
public < E > boolean add(E e){ //只有在调用这个方法时,才会确定变量的类型
//此处E可以理解为变量,但是不是用来记录数据的,而是记录类型的,可以写成:T\K\E\V等
}
②练习
/*需求:
定义一个工具类:ListUtil
类中定义一个静态方法,用来添加多个集合的元素
*/
package com_06Gather._01gatherStructure._02SetTest;
import java.util.ArrayList;
/*需求:
定义一个工具类:ListUtil
类中定义一个静态方法,用来添加多个集合的元素
*/
//编写工具类
public class GenericsTest04_ListUtil {
private ListUtil(){}
//类中定义一个静态方法addAll,用来添加多个集合的元素。
/*
* 参数一:集合
* 参数二~最后:要添加的元素
*
* */
public static<E> void addAll(ArrayList<E> list, E e1,E e2,E e3,E e4){
list.add(e1);
list.add(e2);
list.add(e3);
list.add(e4);
}
//不确定形参类型以及个数的时候可以使用 E ... e
public static<E> void addAll2(ArrayList<E> list, E...e){
for (E element : e) {
list.add(element);
}
}
public void show(){
System.out.println("尼古拉斯·纯情·天真·暖男·阿玮");
}
}
//编写测试类
package com_06Gather._01gatherStructure._02SetTest;
import java.util.ArrayList;
public class GenericsTest04_ListUtilTest {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
GenericsTest04_ListUtil.addAll(list1, "aaa", "bbb", "ccc", "ddd");
System.out.println(list1);
ArrayList<Integer> list2 = new ArrayList<>();
GenericsTest04_ListUtil.addAll(list2,1,2,3,4);
System.out.println(list2);
}
}
(3)接口后面===》泛型接口
//格式
修饰符 interface 接口名 <类型> {
}
//例如
public interface List<E>{
}
①重点:如何使用一个带泛型的接口
方式一:实现类给出具体类型
package com_06Gather._01gatherStructure._02SetTest;
public class GenericsTest05_Interface {
public static void main(String[] args) {
/*
泛型接口的两种使用方式
1.实现类给出库提的接口
2.实现类延续泛型,创建实现类对象时再确定
*/
GenericsTest06_MyArrayList_interface list = new GenericsTest06_MyArrayList_interface();
list.add("aaa");
list.add("bbb");
list.add("ccc");
GenericsTest07_MyArrayList2_interface<String> list2 = new GenericsTest07_MyArrayList2_interface<>();
list2.add("aaa");
list2.add("bbb");
list2.add("ccc");
}
}
package com_06Gather._01gatherStructure._02SetTest;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
//第一种实现类给出具体的类型
public class GenericsTest06_MyArrayList_interface implements List<String> {
@Override
public int size() {
return 0;
}
}
方式二:实现类延续泛型,创建对象时再确定
package com_06Gather._01gatherStructure._02SetTest;
public class GenericsTest05_Interface {
public static void main(String[] args) {
/*
泛型接口的两种使用方式
1.实现类给出库提的接口
2.实现类延续泛型,创建实现类对象时再确定
*/
GenericsTest06_MyArrayList_interface list = new GenericsTest06_MyArrayList_interface();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//第二种,在创建实现类对象的时候确定类型
GenericsTest07_MyArrayList2_interface<String> list2 = new GenericsTest07_MyArrayList2_interface<>();
list2.add("aaa");
list2.add("bbb");
list2.add("ccc");
}
}
package com_06Gather._01gatherStructure._02SetTest;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class GenericsTest07_MyArrayList2_interface<E> implements List<E> {
}
9.泛型的继承和通配符
(1)泛型不具备继承性,但是数据具备继承性
package com_06Gather._01gatherStructure._02SetTest._01Generics;
import java.util.ArrayList;
public class GenericsTest08_extends {
public static void main(String[] args) {
//泛型不具备继承性,但是数据具备继承性
ArrayList<ye> list1 = new ArrayList<>();
ArrayList<fu> list2 = new ArrayList<>();
ArrayList<zi> list3 = new ArrayList<>();
//调用method方法
method(list1);
//泛型不具备继承性
//method(list2);
//method(list3);
//此时泛型里面写的什么数据,那么就只能传递什么类型的数据
//但是数据具备继承性
list1.add(new ye());
list1.add(new fu());
list1.add(new zi());
}
public static void method(ArrayList<ye> list) {
}
}
class ye {
}
class fu extends ye {
}
class zi extends fu {
}
(2)泛型的通配符===》 ?(可以限定类型的范围)
? 也表示不确定的类型,但是可以进行类型的限定
① 限定方式:
★ ? extends E :表示可以传递E或者E所有的子类类型
★ ? super E :表示可以传递E或者E所有的父类类型
②应用场景:
★如果我们在定义类、方法、接口的时候,如果类型不确定,就可以使用泛型类、泛型方法、泛型接口
★如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型通配符
package com_06Gather._01gatherStructure._02SetTest._01Generics;
import java.util.ArrayList;
public class GenericsTest09_extends {
public static void main(String[] args) {
//需求:定义一个方法,形参是一个集合,但是集合中的数据类型不确定
ArrayList<ye1> list1 = new ArrayList<>();
ArrayList<fu1> list2 = new ArrayList<>();
ArrayList<zi1> list3 = new ArrayList<>();
ArrayList<Student> list4 = new ArrayList<>();
//调用method方法
method(list1);
method(list2);
method(list3);
method(list4);
}
//方法一:泛型方法
//弊端:它可以接受任意的数据类型
//希望:本方法虽然不确定类型,但是以后我希望只能传递想要的类型
//此时就可以使用泛型的通配符==》?
// ? 也表示不确定的类型,但是可以进行类型的限定
//限定方式:
// ? extends E :表示可以传递E或者E所有的子类类型
// ? super E :表示可以传递E或者E所有的父类类型
public static<E> void method(ArrayList<E>list){ }
//方法二:使用通配符
public static void method2(ArrayList<? extends ye1>list){}
public static void method3(ArrayList<? super zi1>list){}
}
class ye1 { }
class fu1 extends ye { }
class zi1 extends fu { }
class Student{ }
(3)泛型通配符练习
/*
需求:
定义一个继承结构:
动物
| |
猫 狗
| | | |
波斯猫 狸花猫 泰迪 哈士奇
属性:名字,年龄
行为:吃东西
波斯猫方法体打印:一只叫做XXX的,X岁的波斯猫,正在吃小饼干
狸花猫方法体打印:一只叫做XXX的,X岁的狸花猫,正在吃鱼
泰迪方法体打印:一只叫做XXX的,X岁的泰迪,正在吃骨头,边吃边蹭
哈士奇方法体打印:一只叫做XXX的,X岁的哈士奇,正在吃骨头,边吃边拆家
测试类中定义一个方法用于饲养动物
public static void keepPet(ArrayList<???> list){
//遍历集合,调用动物的eat方法
}
要求1:该方法能养所有品种的猫,但是不能养狗
要求2:该方法能养所有品种的狗,但是不能养猫
要求3:该方法能养所有的动物,但是不能传递其他类型
*/
//定义Animal类以及所有的动物属性
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();
public String toString() {
return "Animal{name = " + name + ", age = " + age + "}";
}
}
//定义猫类继承于Aniaml类
public abstract class Cat extends Animal {
}
//定义狗类继承于Aniaml类
public abstract class Dog extends Animal {
}
//定义猫的子类:狸花猫和波斯猫,定义每个猫特有的属性
//狸花猫
public class LihuaCat extends Cat{
@Override
public void eat(){
System.out.println("一只叫做"+getName()+"的"+getAge()+"岁的狸花猫,正在吃鱼");
}
}
//波斯猫
public class BosiCat extends Cat{
@Override
public void eat(){
System.out.println("一只叫做"+getName()+"的"+getAge()+"岁的波斯猫,正在吃小饼干");
}
}
//定义狗的子类:泰迪和哈士奇,定义每个狗特有的属性
//哈士奇
public class Hashiqi extends Dog{
@Override
public void eat (){
System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的哈士奇,正在吃骨头,边吃边拆家");
}
}
//泰迪
public class Taidi extends Dog{
@Override
public void eat (){
System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的泰迪,正在吃骨头,边吃边蹭");
}
}
//定义测试类
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<BosiCat> list1 = new ArrayList<>();
ArrayList<LihuaCat> list2 = new ArrayList<>();
ArrayList<Taidi> list3 = new ArrayList<>();
ArrayList<Hashiqi> list4 = new ArrayList<>();
keepPet(list1);
keepPet(list2);
keepPet2(list3);
keepPet2(list4);
keepPet3(list1);
keepPet3(list2);
keepPet3(list3);
keepPet3(list4);
}
//要求1:该方法能养所有品种的猫,但是不能养狗
public static void keepPet(ArrayList <? extends Cat> list){}
//要求2:该方法能养所有品种的狗,但是不能养猫
public static void keepPet2(ArrayList <? extends Dog> list){}
//要求3:该方法能养所有的动物,但是不能传递其他类型
public static void keepPet3(ArrayList <? extends Animal> list){}
}
10.泛型总结
(1)什么是泛型
JDK5引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
(2)泛型的好处
★统一数据类型
★把运行时期遇到的问题提前到了编译时间,避免了强制转换可能出现的异常,因为在编译阶段就能确定下来
(3)泛型的细节
★泛型中不能写基本数据类型
★指定反省的具体类型后,传递数据时,可以传入该类型和它的子类型
★如果不写泛型。类型默认是Object类型
(4)哪里定义泛型
★泛型类:在类名里面定义泛型,创建该类对象的时候,确定类型
★泛型方法:在修饰符后面定义方法,调用该方法的时候,确定类型
★泛型接口:在接口名后面定义泛型,实现类确定类型,实现类延续类型
(5)泛型的继承和通配符
★泛型不具备继承性,但是数据具备降级成型
★泛型的通配符:?
===》 ? extends E:
===》 ? super E:
(6)使用场景
★定义类、方法、接口的时候i,如果类型不确定,就可以定义泛型
★如果类型不确定,但是能知道是哪个继承体系中的,可以是使用泛型的通配符