目录
5.泛型
5.1.什么是泛型
1.泛型:
1.1.是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型。
1.2. 泛型也可以看做是一个变量,用来接收数据类型。
1.3.泛型,用来灵活地将数据类型应用到不同的类、方法、接口中。将数据类型作为参数进行传递。可以在类或方法中预支地使用未知的类型。
E e:Element元素
T t:Type类型
2.例如:
ArrayList集合在定义的时候,不知道集合中都会存储什么类型的数据,所以类型使用泛型。
集合可以存储所有引用类型数据,包括对象,包装类,字符串等数据。
创建集合对象时就会确定泛型的数据类型。即ArrayList<String>list=new ArrayList<string>()
public class ArrayList<E>{ //这是ArrayList集合定义(底层实现)
public boolean add(E e)()
public E get(int index)
}
若创建集合对象ArrayList<String>list=new ArrayList<String>();
则会把数据类型作为参数传递,把String赋值给泛型E
public class ArrayList<String>(){
public boolean add(String e){}
public String get(int index){}
}
若创建集合对象ArrayList<Student>list=new ArrayList<Student>();
则会把数据类型作为参数传递,把Student赋值给泛型E
public class ArrayList<Student>{
public boolean add(Student e){}
public Student get(int index){}
}
一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
5.2.使用泛型与否的区别
1.创建集合对象时使用泛型:
* 1.好处:
* 1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
* 2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错)。
* 2.弊端:
* 泛型是什么类型,只能存储什么类型的数据
* 2.创建集合对象时不用泛型:
* 1.好处:
* 集合不使用泛型,默认的类型就是Object类型,可以存储任意类型数据。(基本类型转为包装类)
* 2.弊端:
* 不安全,会引发异常。public class GenericDemo2 { public static void main(String[] args) { show2(); } /** * 1.创建集合对象时使用泛型: * 1.好处: * 1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型 * 2.把运行期异常(代码运行之后会抛出的异常), * 提升到了编译期(写代码的时候会报错)。 * 2.弊端: * 泛型是什么类型,只能存储什么类型的数据 */ private static void show1(){ ArrayList<String>list=new ArrayList<>(); list.add("abc"); //list.add(1);集合使用泛型后,这个时候就报错了,运行期异常提升到编译期。 //使用迭代器遍历list集合 Iterator<String>it=list.iterator(); while(it.hasNext()){ String s = it.next(); System.out.println("s长度"+s.length()); } } /** * 2.创建集合对象时不用泛型: * 1.好处: * 集合不使用泛型,默认的类型就是Object类型,可以存储任意类型数据。 * 2.弊端: * 不安全,会引发异常。 */ private static void show2(){ ArrayList list=new ArrayList(); list.add("abc"); list.add(1); //使用迭代器遍历list集合 //获取迭代器(即调用集合中的iterator()方法,并用Iterator接口接收) Iterator it=list.iterator(); //使用迭代器中方法hasNext和next遍历集合 while(it.hasNext()){ //取出元素也是Object类型 //即不使用泛型的集合可存储各种类型的数,当从集合中取出时均以Object类型取出 Object obj = it.next(); System.out.println(obj); //若想要使用String类特有的方法length获取字符串的长度,是不能使用的, //多态 Object obj="abc"; //需要向下转型 //会抛出ClassCastException类型转换异常,无法把Iterator类型转换为String类型 String s=(String)obj; System.out.println(s.length());//抛出异常 } } }
5.3.泛型在类、方法和接口中使用
1.定义含有泛型的类
class ArrayList<E>{
public boolean add(E e){}
public E get(int index){}
.....
}
含有泛型的类什么时候确定泛型的数据类型:创建对象的时候确定泛型的类型,将数据类型作为参数传递给泛型2.定义含有泛型的方法
public <M> void method01(M m){
System.out.println(m);
}
public static <S> void method02(S s){
System.out.println(s);
}
含有泛型的方法什么时候确定泛型数据类型:在调用方法时确定泛型数据类型。传递什么类型参数,泛型就是什么类型
3.定义含有泛型的接口
public interface GenericDemo5Interface<I>{
public abstract void method(I i);
}
4.含有泛型的接口的两种使用方式
1.定义接口的实现类,实现接口,指定接口泛型。
2.定义接口的实现类,实现接口,不指定具体接口泛型。
接口使用什么泛型,实现类就使用什么泛型,类跟着接口走。相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型。
public class GenericDemo5InterfaceImpl1 implements GenericDemo5Interface<String> {
public void method(String s){ //定义接口的实现类,要重写接口的抽象方法
System.out.println(s);
}
public class GenericDemo5InterfaceImpl2<I> implements GenericDemo5Interface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
}
含有泛型的接口什么时候确定泛型的类型:定义类时确定泛型的类型或创建对象时确定泛型的类型两种。5.泛型通配符的使用:不能在创建集合对象时使用,只能作为方法的参数使用
:public static void printArrayList(ArrayList<?> list){}
5.3.1.泛型在类中使用
/**
* 1.定义一个含有泛型的类
* 2.泛型是一个未知的数据类型,当我们不确定什么数据类型的时候,可以使用泛型。
* 3.泛型可以接受任意的数据类型,可以使用Integer,String,Student...
* 4.创建对象的时候确定泛型的类型。
*/
public class GenericDemo3Class<E> {
private E name;
public E getName(){
return name;
}
public void setName(E name){
this.name=name;
}
}
public class GenericDemo3class1 {
public static void main(String[] args) {
//不使用泛型则默认获取的数据为Object类型
GenericDemo3Class gc = new GenericDemo3Class();
Object obj = gc.getName();
//创建含泛型的类对象,泛型使用Integer类型(创建对象时确定泛型类型)
GenericDemo3Class<Integer> gc2 = new GenericDemo3Class<>();
gc2.setName(1);
Integer name = gc2.getName();
System.out.println(name);
//创建含泛型的类对象,泛型使用String
GenericDemo3Class<String> gc3 = new GenericDemo3Class<>();
gc3.setName("小明");
String name1 = gc3.getName();
System.out.println(name1);
}
}
5.3.2.泛型在方法中使用
/**
* 1.定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间。
* 2.格式;
* 修饰符<泛型> 返回值类型 方法名(参数列表(使用泛型)){
* 方法体;
* }
* 3.含有泛型的方法,在调用方法的时候确定泛型的数据类型。
* 传递什么类型的参数,泛型就是什么类型。
*/
public class GenericDemo4Method {
/**
* 定义一个含有泛型的方法
*/
public <M> void method01(M m){
System.out.println(m);
}
/**
* 定义一个含有泛型的静态方法
* 注意:静态方法不建议通过创建对象调用
* 一般使用类名来调用静态方法。
*/
public static <S> void method02(S s){
System.out.println(s);
}
}
/**
* 测试含有泛型的方法
*/
public class GenericDemo4Method1 {
public static void main(String[] args) {
//创建GenericMethod对象
GenericDemo4Method gm = new GenericDemo4Method();
/**
* 调用含有泛型的方法method01
* 传递什么类型,泛型就是什么类型,参数可以为任意类型
*/
gm.method01(10);
gm.method01("abc");
gm.method01(8.8);
gm.method01(true);
//静态方法,通过类名.方法名(参数)可以直接使用。
GenericDemo4Method.method02("静态方法");
GenericDemo4Method.method02(1);
}
}
5.3.3.泛型在接口中使用
/**
* 定义含有泛型的接口
*/
public interface GenericDemo5Interface<I>{
public abstract void method(I i);
}
/**
* 1.含有泛型的接口:第一种使用方式:定义接口的实现类,实现接口,指定接口泛型
* 例如:之前学过迭代接口,该接口中定义了next()方法获取下一个元素
* public interface Interator<E>{
* E next();
* }
* 而Scanner类实现了Iterator接口,并指定接口泛型为String,
* 所以重写的next()方法泛型默认就是String
* public final class Scanner implements Interator<String>{
* public String next(){}
* }
* 类名最后是Impl表示该类是接口的实现类
*/
public class GenericDemo5InterfaceImpl1 implements GenericDemo5Interface<String> {
public void method(String s){ //定义接口的实现类,要重写接口的抽象方法
System.out.println(s);
}
}
/**
* 1.含有泛型的接口第二种使用方式:定义接口的实现类,实现接口,不指定具体接口泛型,
* 接口使用什么泛型,实现类就使用什么泛型,类跟着接口走。
* 相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型。
* 2.例如List接口的实现类ArrayList:
* public interface List<E>{ //List接口定义
* boolean add(E e);
* E get(int index);
* }
* public class ArrayList<E> implements List<E>{ //List接口实现类定义
* public boolean add(E e){}
* public E get(int index){}
* }
*
*/
public class GenericDemo5InterfaceImpl2<I> implements GenericDemo5Interface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
}
5.3.4.使用泛型注意事项
泛型不存在继承关系 Collection<Object> list = new ArrayList<String>();这种是错误的。
5.4.泛型通配符
5.4.1.什么时候使用泛型通配符
当使用泛型类或者泛型接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。比如,定义一个方法能遍历所有类型的ArrayList集合,这个时候我们不知道ArrayList集合使用什么数据类型,因此要用泛型的通配符。
5.4.2.泛型通配符的弊端(缺陷)
一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。 此时只能接收数据,不能往该集合中存储数据。
5.4.3.泛型通配符的使用和注意事项
1.泛型的通配符 ?:代表任意的数据类型
2.不能在创建集合对象时使用,只能作为方法的参数使用.ArrayList<?> list = new ArrayList<?>();报错
public class GenericDemo6 { public static void main(String[] args) { //创建集合 ArrayList<Integer> list01 = new ArrayList<>(); list01.add(1); list01.add(2); ArrayList<String> list02 = new ArrayList<>(); list02.add("a"); list02.add("b"); //不能在创建集合对象时使用,只能作为方法的参数使用 //ArrayList<?> list = new ArrayList<?>();报错 } /** * 定义一个方法,能遍历所有类型的ArrayList集合 * 这时我们不知道ArrayList集合使用什么数据类型, * 可以使用泛型的通配符?来接收数据类型。 * 注意:泛型是没有继承概念的,虽然包装类和字符串都继承Object, * 但不能用Object作为泛型来分别接收包装类类型和字符串类型。 * 只能使用泛型通配符。 */ public static void printArrayList(ArrayList<?> list){ //使用迭代器遍历集合(集合是什么类型,迭代器就是什么类型) Iterator<?> it = list.iterator(); while(it.hasNext()){ //it.next()方法取出的元素都是Object类型,可以接收任意数据类型 Object o = it.next(); System.out.println(o); } } }
5.5.泛型通配符高级使用-受限范型
泛型通配符的高级使用--只适用于在方法的参数中使用泛型通配符。
1.之前设置泛型时候,实际上是可以任意设置的,只要是类就可以设置。但是在Java泛型中可以指定泛型的上限和下限。
2.泛型的上限:
格式:集合类型名称 <? extends 类> 对象名称
意义:代表使用的泛型?只能是E类型本身或者其子类。
3.泛型的下限:
格式:集合类型名称 <? super 类> 对象名称
意义:代表使用的泛型?只能是E类型本身或者其父类。
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类 Integer extends Number extends Object* 泛型通配符的高级使用:受限泛型 * 1.泛型的上限限定: ? extends E 代表使用的泛型?只能是E类型本身或者其子类 * 2.泛型的下限限定: ? super E 代表使用的泛型?只能是E类型本身或者其父类。 public class GenericDemo7 { public static void main(String[] args) { //创建Collection集合(因为它是接口,要用接口指向它的实现类来创建对象) Collection<Integer>list1=new ArrayList<>(); Collection<String>list2=new ArrayList<>(); Collection<Number>list3=new ArrayList<>(); Collection<Object>list4=new ArrayList<>(); //通过泛型通配符获取不同数据类型的集合对象 getElement1(list1);//Integer类是Number类的子类 getElement1(list2);//报错,String类不是Number的子类 getElement1(list3);//Number类是Number类本身 getElement1(list4);//报错,Object类是Number父类而不是子类 getElement2(list1);//报错,Integer类不是Number类的父类 getElement2(list2);//报错,String类不是Number类的父类 getElement2(list3);//Number类是Number类本身 getElement2(list4);//Object类是Number类父类 /** * 类与类之间的继承关系: * Integer extends Number extends Object * String extends Object */ } /** * 在创建含泛型通配符的方法时,只能作为方法的参数使用 * 方法的参数定义一个集合对象用来接收集合,定义的这个集合使用受限泛型。 */ //设置泛型的上限:此时的泛型为?,必须是Number类型或者Number类型的子类 public static void getElement1(Collection<? extends Number>coll){ } //设置泛型的下限:此时的泛型为?,必须是Number类型或者Number类型的父类 public static void getElement2(Collection<? super Number>coll){ } }