0
点赞
收藏
分享

微信扫一扫

Java面向对象10-泛型

分湖芝蘭 2022-03-15 阅读 55

什么是泛型(Generic)

泛型就相当于标签
形式:< >
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际村的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,因此此时把元素类型设计成一个参数,这个类型参数叫做泛型。
Collection, List, ArrayList,这个就是类型参数,即泛型。

没有泛型的时候

package msb.javase.oop.javase_220313_generic_test;

import java.util.ArrayList;
import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(73);
        list.add(92);
        list.add(89);
        list.add(90);
        list.add("sp"); //不小心录入了一个字符串数据
        for(Object a:list){
            System.out.println(a);
        }
    }
}

如果不使用泛型的话,缺点:
一般我们在使用的时候基本傻姑娘往集合存入的都是相同类型的数据–》便于管理,所以现在什么引用数据类型都可以存入集合(底层Object类型)–》不方便。
对此进行限制,便有了泛型。

泛型 ---- 类似于垃圾分类的概念,只有限定类型的垃圾才可以放到对应的垃圾桶。

集合中使用泛型:(JDK1.5之后)

package msb.javase.oop.javase_220313_generic_test;

import sun.rmi.server.InactiveGroupException;

import java.util.ArrayList;
import java.util.List;

public class Test01 {
    public static void main(String[] args) {
    	//1、在编译的时候就会对类型进行检查,不是泛型对应的类型就不可以添加入这个集合
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(73);
        list.add(92);
        list.add(89);
        list.add(90);
        //list.add("sp");
        //2、在遍历时,类型是确定的,不需要再是Object类型
        for(Integer a:list){
            System.out.println(a);
        }
    }
}

加入泛型的优点:
1、在编译的时候就会对类型进行检查,不是泛型对应的类型就不可以添加入这个集合
2、在遍历时,类型是确定的,不需要再是Object类型

泛型总结
(1)JDK1.5以后
(2)泛型实际就是一个<>引起来的参数类型,具体在使用的时候才确定类型

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

(3)使用了泛型以后,可以确定集合中存放数据的类型,在编译时期就可以检查出来
(4)使用泛型可能觉得麻烦,实际使用了泛型才会简单,后续的遍历等操作简单
(5)泛型的类型:都是引用数据类型,不能是基本数据类型
(6)ArrayList list = new ArrayList();在JDK1.7以后可以写为:ArrayList list = new ArrayList<>(); — 钻石运算符

自定义泛型

泛型类、泛型接口

泛型类定义

package msb.javase.oop.javase_220313_generic_test;

//GenericTest是一个普通类
//GenericTest<E>是一个泛型类,和普通类的区别
//<E>是参数类型,是不确定的(占位)
//但可以确定的是,E是一个引用数据类型

import com.sun.org.apache.xpath.internal.operations.String;

public class GenericTest<E> {
    int age;
    String name;
    E sex;
    public void a(E n){
        System.out.println(n);
    }
    public void b(E[] m){
        System.out.println(m.toString());
    }
}

泛型类使用

package msb.javase.oop.javase_220313_generic_test;

public class Test03 {
    public static void main(String[] args) {
        //对GenericTest进行实例化
        //(1)实例化时不使用泛型,如果实例化的时候不明确指定类的泛型,那么认为此泛型为Object类型
        GenericTest gt1 = new GenericTest();
        gt1.a(12);//此时E相当于Object
        gt1.b(new java.lang.String[]{"a","b"});

        //(2)实例化时使用泛型 --- 推荐方式
        GenericTest<java.lang.String> gt2 = new GenericTest<>();
        gt2.a("ccc");
        gt2.b(new java.lang.String[]{"a","b"});
    }
}

继承泛型类

//父类指定泛型
class SubGenericTest extends GenericTest<Integer>{
    
}
//父类不指定泛型
//如果父类不知钉泛型,那么子类也会变成一个泛型类,那这个E的类型可以在创建子类对象的时候确定
class SubGenericTest2<E> extends GenericTest<E>{

}

应用场景(泛型类、泛型接口)

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

注意事项

(1)泛型类可以定义多个

package msb.javase.oop.javase_220313_generic_test;

import com.sun.org.apache.xpath.internal.operations.String;

public class GenericTest2<E,F> {
    int age;
    E name;
    F sex;
    public void a(E n){
        System.out.println(n);
    }
    public void b(F[] m){
        System.out.println(m.toString());
    }
}

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

(2)泛型类构造器不可以写成

    public GenericTest2<E,F>(){ //不可以加<E,F>
    }

(3)不同的泛型引用类型不可以相互赋值

        ArrayList<String> list1 = null;
        ArrayList<Integer> list2 = null;
        list2 = list1; //错误,在编译期就被限制了

(4)泛型如果不指定,那么就会被擦除,泛型对应的类型变为Object类型

        GenericTest gt1 = new GenericTest();
        gt1.a(12);//此时E相当于Object

(5)泛型类中的静态方法不能使用类的泛型(static方法优先于对象存在,而泛型类型在创建对象的时候才确定

(6)不能直接使用E[]的创建

E[] e = new E[10]; //错误
E[] e = (E[])new Object[10]; //可以通过强制转换类型的方式来做

泛型方法

定义

    public <T> void c(T t){ //泛型方法的参数类型 和 当前类的泛型无关
    }

调用 — 泛型方法的参数类型在调用方法的时候才确定

package msb.javase.oop.javase_220313_generic_test;

public class GenericTest2<E,F> {
    int age;
    E name;
    F sex;
    public void a(E n){
        System.out.println(n);
    }
    public <T> void c(T t){
    }
}
class C{
    public static void main(String[] args) {
        GenericTest2<java.lang.String> gt = new GenericTest2<>();
        gt.a("a");
        gt.c("c");
        gt.c(12);
        gt.c(true);
    }
}

注意事项

(1)泛型方法可以是静态方法

    public static <T> void c(T t){ //正确,泛型方法,可以是静态方法
    }
    public static void d(E n){ //错误,不是泛型方法,不能是静态方法
    }    

(2)泛型参数存在继承关系的情况

package msb.javase.oop.javase_220314_generic_test2;

import java.util.ArrayList;
import java.util.List;

public class Test01 {
    public static void main(String[] args) {
        Object o = new Object();
        String s = new String();
        o = s; //多态的一种形式,父类引用指向子类对象

        Object[] o2 = new Object[10];
        String[] s2 = new String[10];
        o2 = s2; //多态的一种形式,父类引用指向子类对象(数组也支持)

        List<Object> list = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        //list = list2; //错误,泛型不支持(A和B是子类父类关系,但是G<A>和G<B>不存在继承关系,是并列关系)
    }
}

总结:A和B是子类父类关系,但是G和G不存在继承关系,是并列关系,所以不支持多态

解决此问题的办法:通配符

package msb.javase.oop.javase_220314_generic_test2;

import java.util.ArrayList;
import java.util.List;

public class Test02 {
    public static void main(String[] args) {
        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<Integer> list3 = new ArrayList<>(); //并列关系
        
        List<?> list = new ArrayList<>();
        list = list1;
        list = list2;
        list = list3; //父子关系
    }
}

(1)在没有通配符的情况下,下面的方式属于重复定义

public class Test03 {
    public void a(List<Object> list){
    }
    public void a(List<String> list){
    }
    public void a(List<Integer> list){
    }
}

(2)引入通配符

A和B是子类父类的关系,G< A > 和 G< B > 不存在子类父类关系,是并列的,引入通配符?之后,G<?>就变成了G< A >和G< B >的父类

package msb.javase.oop.javase_220314_generic_test2;

import java.util.ArrayList;
import java.util.List;

public class Test03 {
/*    public void a(List<Object> list){
    }
    public void a(List<String> list){
    }
    public void a(List<Integer> list){
    }*/
    public void a(List<?> list){
        public void a(List<?> list){
        //1、遍历
        for(Object a:list){
            System.out.println(a);
        }
        //2、数据写入
        //list.add("abc");//报错,不能随意写入特定类型数据
        list.add(null);//只能写入null
        //3、数据读取
        Object o = list.get(0);//接收数据类型只能为Object
    }
    }
}

class Test{
    public static void main(String[] args) {
        Test03 t3 = new Test03();
        t3.a(new ArrayList<Integer>());
        t3.a(new ArrayList<String>());
        t3.a(new ArrayList<Object>());
    }
}

注意:内部遍历的时候用Object即可,不用?

泛型受限

package msb.javase.oop.javase_220314_generic_test3;

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        //虽然泛型有继承关系,但a、b、c是并列关系
        List<Object> a = new ArrayList<>();
        List<Person> b = new ArrayList<>();
        List<Student> c = new ArrayList<>();
        //开始使用泛型受限:
        //(1)泛型的上限
        /*List<? extends Person>
        就相当于:
        List<? extends Person>是List<Person>的父类,是List<Person的子类>的父类*/
        List<? extends Person> list = null;
        //list = a;//报错
/*        list = b;
        list = c;*/
        //(2)泛型的下限
        /*List<? super Person>
        就相当于:
        List<? super Person>是List<Person>的父类,是List<Person的父类>的父类*/
        List<? super Person> list2 = null;
        list2 = a;
        list2 = b;
        //list2 = c;//报错
    }
}

举报

相关推荐

0 条评论