JAVA---泛型

一、什么是泛型

        Java的泛型 (generics) 是在JDK5中推出的新概念,在泛型推出之前,程序员需要构建一个元素为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则会发生ClassCastException (类转换异常)

        而泛型提供了编译时类型安全监测机制,允许我们在编译时检测到非法的类型数据结构,它的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数 (形参),

        以ArrayList举例子来说,通过查看ArrayList源码可以看到,ArrayList中可以存放任意的类型是因为有一个泛型<E>,当new一个ArrayList并在泛型中放任意的类型之后此时这个ArrayList就只能存泛型中存放的这个类型的对象,这就是泛型的作用

        泛型的好处就在于可以让类型的存放更安全并且可以避免强制类型的转换不会报ClassCastException


二、泛型类

1、语法定义

class 类名<泛型标识, 泛型标识, ...>{

    private 泛型标识 变量名;

    .....

}

使用语法

类名<具体数据类型> 对象名 = new 类名<具体数据类型>();

java1.7之后,后面的<>中的具体的数据类型可以省略不写

类名<具体的数据类型> 对象名 = new 类名<>();

注意事项:

泛型类,如果没有指定具体的数据类型,此时操作类型是Object

 <具体数据类型> 泛型中只能存放包装类

 泛型类在逻辑上可以看成是多个不同的类型,但实际上都是相同类型

例:

public class Test1<T> {
    private T key;

    public T getKey() {
        return key;
    }

    public void setKey(T key) {
        this.key = key;
    }
}

public class MainMethod {
    public static void main(String[] args) {
        Test1<String> test1 = new Test1<>();
        test1.setKey("abc");
        System.out.println("test1存的key为:" + test1.getKey());
        System.out.println("test1的类型为:" + test1.getClass().getSimpleName());
        System.out.println("test1存的key的类型为:" + test1.getKey().getClass().getSimpleName());
    }
}

同一泛型类,根据不同的数据类型创建的对象,本质上是同一类型

public class MainMethod {
    public static void main(String[] args) {
        Test1<String> test1 = new Test1<>();
        test1.setKey("abc");
        System.out.println("test1的类型为:" + test1.getClass());

        Test1<Integer> test2 = new Test1<>();
        test2.setKey(100);
        System.out.println("test2的类型为:" + test2.getClass());

        System.out.println(test1.getClass() == test2.getClass());
    }
}

多个泛型

public class Test1<T,V> {
    private T key;
    private V value;

    public T getKey() {
        return key;
    }

    public void setKey(T key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }
}

public class MainMethod {
    public static void main(String[] args) {
        Test1<String,Integer> test1 = new Test1<>();
        test1.setKey("number");
        test1.setValue(100);
        System.out.println(test1.getKey() + ":" + test1.getValue());
    }
}

2、常用的泛型标识: T、E、K、V、?

T (type) 表示具体的一个java类型   常用在定义单个对象或者单个方法时

E (element) 代表元素   常在集合中使用

K V  分别代表java键值中的Key和Value   常用在类似于map的集合中

?表示不确定的 java 类型   常在上下限中使用


三、泛型类派生子类

1、语法定义

子类也是泛型类,子类和父类的泛型类型要一致

 class Children<T> extends Father<T>

子类不是泛型类,父类要明确泛型的数据类型

class Children extends Father<具体类型>

2、使用场景

 

 可以看出使用instanceof之后调用的是children的属性

 要是父类.属性调用的就是父类的

 


四、泛型接口

1、语法定义

interface 接口名<泛型标识,泛型标识, ...>{

   泛型标识 方法名();

   .....

}

2、泛型接口的使用

实现类不是泛型类,接口要明确数据类型

实现类也是泛型类,实现类和接口的泛型类型要一致

例:

public interface InMethod<T> {
    T getKey(T t);
}
//如果此时实现接口后泛型中不指定具体类型那么重新接口的方法和派生子类一样是Object
public class claMethod implements InMethod{


    @Override
    public Object getKey(Object o) {
        return null;
    }
}

//如果此时实现接口后泛型中指定具体的类型那么重写接口方法之后返回的就是定义的类型
public class claMethod implements InMethod<Integer>{


    @Override
    public Integer getKey(Integer integer) {
        return null;
    }
}

实现接口之后重写一下接口中的方法

public class ClaMethod implements InMethod<Integer>{

    @Override
    public Integer getKey(Integer integer) {
        return integer;
    }
}
public class MainMethod {
    public static void main(String[] args) {
        ClaMethod claMethod = new ClaMethod();
        Integer num = claMethod.getKey(100);
        System.out.println(num.getClass().getSimpleName());
    }
}

类实现接口并使用了接口中的方法后因为类没有定义泛型所以使用的类型是接口的类型

//接口中的泛型是实现类中泛型的其中一个就可以
public class ClaMethod<T,K,E> implements InMethod<K>{
    
    @Override
    public K getKey(K k) {
        return null;
    }
}


五、泛型方法

1、语法定义

修饰符 <T, E, ...> 返回值类型 方法名(形参列表) {

        方法体 ...

}

【注意】

·   public 与 返回值中间<T> 非常重要,可以理解为声明此方法为泛型方法。

·  只有声明了<T>的方法才是泛型方法,泛型类中 使用了泛型的成员方法并不是泛型方法

·  <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T

·  与泛型类的定义一样,此处T可以随便写为任意标识,如 T、E、K、V等形式的参数常用于表示     泛型

例:

public class ClaMethod<T,K,E>{

    public <T> T getGeneric(T t){
        return t;
    }
}
public class MainMethod {
    public static void main(String[] args) {
        ClaMethod claMethod = new ClaMethod();
        String str = "abc";
        System.out.println(claMethod.getGeneric(str).getClass().getSimpleName());
    }
}

//要是调用的不是void方法 可以传多个类型 return的和上边返回的类型要一致
public <T,K> K GG(K k){
        return k;
    }

2、静态的泛型方法

public class ClaMethod<T,K,E>{
    //静态的泛型方法采用多个泛型类型
    public static <T,K,E> void getGenericType(T t,K k){
        System.out.println(t+"\t"+t.getClass().getSimpleName());
        System.out.println(k+"\t"+k.getClass().getSimpleName());
    }
}
public class MainMethod {
    public static void main(String[] args) {
        //static使用类名.方法名
        ClaMethod.getGenericType("abc",100);
    }
}

 3、泛型方法与可变参数

public <E> void print(E... e){

  for(E date : e){

     System.out.println(date);

  }

}

public class ClaMethod{

    public<E> void printE(E... e){
        for (E data : e) {
            System.out.println(data);
        }
    }
}
public class MainMethod {
    public static void main(String[] args) {
        ClaMethod claMethod = new ClaMethod();
        claMethod.printE("a","b","c");
    }
}

4、泛型方法和泛型类的区别

泛型类,是在实例化类的时候指明泛型的具体类型

泛型方法,是在调用方法的时候指明泛型的具体类型

5、泛型方法总结

泛型方法能使方法独立于类而产生变化

泛型可以使用多个,要是没有void 就return其中一个泛型,要是有void那么就正常输出就好

静态的泛型方法和普通泛型方法的区别是 静态的泛型方法是在泛型前多加一个static


六、类型通配符

1、定义

类型通配符一般是使用 "?" 代替具体的类型实参,所以,类型通配符是类型实参,而不是类型形参

一般和上下限进行配合使用

2、示例

public class ListMethod {
    private String listKey;

    public ListMethod(String listKey) {
        this.listKey = listKey;
    }

    @Override
    public String toString() {
        return "ListMethod{" +
                "listKey='" + listKey + '\'' +
                '}';
    }
}

public class MainMethod {
    public static void main(String[] args) {
        ListMethod listMethod1 = new ListMethod("a");
        ListMethod listMethod2 = new ListMethod("b");
        ListMethod listMethod3 = new ListMethod("c");


        ArrayList<ListMethod> list = new ArrayList<>();
        list.add(listMethod1);
        list.add(listMethod2);
        list.add(listMethod3);
        MainMethod.getMethod(list);
    }

    public static void getMethod(List<?> list){
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

3、类型通配符的上限

语法

类/接口 <? extends 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型

首先我们定义一下继承关系 如图所示 

MaxCat类MiniCat类 分别继承了 Cat类 Cat类继承了Animal类 

上限代表的意思是  能填写的类的级别最高就到extends后边的那个实体类 (包括本身)

public class MainMethod {
    public static void main(String[] args) {

        ArrayList<Animal> animalList = new ArrayList<>();
        ArrayList<Cat> catList = new ArrayList<>();
        ArrayList<MaxCat> maxList = new ArrayList<>();
        ArrayList<MiniCat> miniCatList = new ArrayList<>();

        //上限 -> 只能添加继承后边的实参对象的子类 
//        showExtendsList(animalList);
        showExtendsList(catList);
        showExtendsList(maxList);
        showExtendsList(miniCatList);
    }

    public static void showExtendsList(ArrayList<? extends Cat> list){

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

    }
}

【重要】

类/接口 <? extends 实参类型> 这个东西叫泛型列表 在上限中泛型列表里边是不允许添加其他元素的

因为现在有这么一种情况,我们都知道java中要是创建一个类的话要从它的父类开始创建,一直到当前类,

以上图例子进行说明,那么假如现在我调用showExtendsList方法 在该方法中进行添加元素,main方法中我们可以看到,上限因为可以存的是Cat的子类,现在有这么一种情况在MaxCat和MiniCat下分别又有四个子类 BlackMaxCat   YellowMiniCat   BlackMiniCat   YellowMiniCat   如下图

现在四个类中都分别有一个各自类的方法依次为 A B C D

 那么当我添加BlackMaxCat的时候 万一泛型列表中存放的是其他的 YellowMaxCat这三个类 那么创建对象的时候只能创建父类,方法找不到就会报错,说白了我们无法确定最小的类是什么,为了安全考虑在上限的泛型列表中不允许添加子类对象,那为什么添加extends后边对象的父类也不行呢,同样是因为继承的原因,我们不确定这个父类是一个总体的父类还是某一个节点的父类,所以说同样是为了安全考虑在上限的泛型列表中不允许添加父类对象,综上所述,上限列表中是不允许添加对象的

4、类型通配符的下限

语法

类/接口<? super 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的父类类型

继承关系如图所示

下限代表的意思是  能填写的类的级别最低就到super后边的那个实体类 (包括本身)

public class MainMethod {
    public static void main(String[] args) {

        ArrayList<Animal> animalList = new ArrayList<>();
        ArrayList<Cat> catList = new ArrayList<>();
        ArrayList<MaxCat> maxList = new ArrayList<>();
        ArrayList<MiniCat> miniCatList = new ArrayList<>();


        //下限
        showSuperList(animalList);
        showSuperList(catList);
//        showSuperList(maxList);
//        showSuperList(miniCatList);

    }




    public static void showSuperList(ArrayList<? super Cat> list){
//        list.add(new Animal());
        list.add(new Cat());
        list.add(new MiniCat());
        list.add(new MiniCat());
        for (Object superList: list) {
            System.out.println(superList);
        }
    }


}

【重要】

类/接口 <? super 实参类型> 这个东西叫泛型列表 在下限中泛型列表里边允许添加super 后边实体类的子类 (包括本身)

因为我们知道我们添加的类都是super 后边这个类的父类,我在泛型列表里边添加子类 new对象的时候父类也会被创建出来 不会受到影响,不能存父类是因为不能排除还有其他类和当前类都继承了同一个父类的情况,存父类的时候 因为下边的子类无法创建了所以又会报错造成不安全

【总结】

对于泛型列表可以把类的继承关系想象成一个树结构

上限可以存元素存的是extends 后边类的子类 但上限的泛型列表不能存元素

下限可以存元素存的是 super 后边类的父类   下限列表可以存元素存的是 super后边类的子类


七、类型擦除

        泛型是java1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好的和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进JVM之前,与泛型相关的信息会被擦除掉,我们称之为--类型擦除

1、类的类型擦除

无限制类型擦除

没有限制时 最后jvm运行时识别的都是Object

有限制类型擦除

有限制时,jvm运行时上限识别的是限制的类型  下限识别的是Object

2、方法的类型擦除

方法的类型擦除,jvm运行时上限识别的是限制的类型  下限识别的是Object

3、接口的类型擦除

【注意】

【桥接方法】

实现了泛型接口的方法在类型擦除之后会生成两个方法 一个是生成限制类型的方法一个是桥接方法桥接方法指的是接口中的方法进行类型擦除之后生成的Object方法要在实现类的方法中进行一个对应,换句话说,类型擦除后 接口中的方法是Object方法 实现类中的方法一个是限制的另一个还需要一个与接口的Object方法相对应的 返回值的类型是Object类型,但return的类型是限制类的类型  


八、泛型数组

1、泛型数组的创建

可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象

可以通过java.lang.reflect.Array的newInstance(Class<T> ,int) 创建T[]数组

例:

这么写是错误的 ,正确的写法如下图

或者可以是如下写法

区别在于没有把原生对象暴露出来而是采用了直接将匿名对象赋给泛型数组

【注意】new ArrayList 后边没有<> 证明这是ArrayList类型的数组而不是ArrayList集合数组

public class MainMethod {
    public static void main(String[] args) {
        //错误
        ArrayList<String>[] arrayList = new ArrayList<String>[5];
        //正确
        ArrayList<String>[] arrayList1 = new ArrayList[5];
    }
}

2、对象中创建泛型数组

public class StrClass<T> {
    private T[] array = new T[5];
}

这种方式是错误的因为我们不知道传的类型是什么类型,万一如上边一样传一个ArrayList<String>那么一定会报错的

正确的方式如下

使用java中Array类的newInstance方法利用反射创建一个对象放在构造器当中从而创建一个数组

public class StrClass<T> {

    private T[] array;

    public StrClass(Class<T> strClass,int length){
        array = (T[])Array.newInstance(strClass,length);
    }

    public void setArray(T[] array) {
        this.array = array;
    }
    public T[] getArray() {
        return array;
    }
}

public class MainMethod {
    public static void main(String[] args) {
        StrClass<String> strClass = new StrClass<>(String.class,10);
        String[] str = {"a","b","c"};
        strClass.setArray(str);
        
        //调用Arrays.toString方法输出setArray的值
        System.out.println(Arrays.toString(strClass.getArray()));
    }
}


九、反射常用的泛型类

Class<T>

Constructor<T>

public class MainMethod {
    public static void main(String[] args) {

        //Class<T>
        Class<String> stringClass  = String.class;


        //Constructor<T>
        try {
            Constructor<String> constructor = stringClass.getConstructor();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: jmu-java-05集合(泛型)-10-generalstack是关于Java泛型中的通用栈的学习内容。通用栈是一种可以存储任意类型数据的栈结构,通过泛型的方式实现。在学习中,我们可以了解到通用栈的实现原理、使用方法以及注意事项等内容,帮助我们更好地理解和应用Java泛型。 ### 回答2: JMU-Java-05集合(泛型)-10-GeneralStack是一个Stack(栈)的实现类,使用Java中的泛型进行定义,可以存储任何类型的数据。 在该类中,使用一个Object类型的数组进行存储元素,并通过一个整型变量top来表示当前栈顶元素的下标。在push(入栈)方法中,先将top加一,然后将元素存储在数组中,实现了入栈的功能。在pop(出栈)方法中,先判断栈是否为空,若为空则抛出栈空异常,否则将top减一,并返回数组中相应的元素,实现了出栈的功能。其他方法如isEmpty(判断栈是否为空)、isFull(判断栈是否已满)和size(获取栈中元素个数)也在该类中实现。 该类的泛型定义使得我们可以使用该类存储任何类型的数据,而不需要在定义类时指定数据类型,提高了其灵活性和可复用性。在使用时,我们只需要在创建对象时传入相应的数据类型,如:GeneralStack<Integer> stack = new GeneralStack<Integer>();即可。 该类还实现了Iterable接口,使得该类可以使用foreach循环进行遍历操作,方便了我们对栈中元素的操作。同时,该类还通过对数组的动态扩容,解决了数组固定大小的限制问题,从而提高了该栈类的通用性和易用性。 总之,JMU-Java-05集合(泛型)-10-GeneralStack是一个使用泛型实现的通用栈类,可以存储各种类型的数据,并提供了常用的栈操作方法,具有较高的可复用性与适用性。 ### 回答3: jmu-java-05集合(泛型)-10-generalstack,意为基于泛型的栈实现。 首先我们需要了解什么是泛型泛型Java SE 5引入的一个新特性,它可以让我们在编写代码时定义一些未知的类型参数,以达到代码的复用和类型安全的目的。对于集合类或者其他容器类而言,适用于任何类的容器的需求是普遍存在的,这时就可以应用泛型。 在Java泛型使用<>标识,其语法格式如下: ```java public class 类名<类型参数列表> { //成员变量、方法等 } ``` 类型参数列表是由逗号隔开的参数列表,可以理解为未知类型的占位符。这样的好处是可以在编写集合类时,避免出现类型转换等一系列问题,提高程序的可读性。 而在jmu-java-05集合(泛型)-10-generalstack中,则是基于泛型实现的数据结构——栈。栈是一种后进先出(LIFO)的数据结构,它只允许在栈顶添加或删除元素,因此操作非常简单、快速并且高效。 在该实现中,栈的元素可以是任意类型,数据元素入栈则是通过 push() 方法实现的,出栈则是通过 pop() 方法实现的。其中,push() 方法用于向栈中添加新的元素,pop() 方法用于弹出并返回栈顶的元素。同时,还提供了 isEmpty() 方法、size() 方法等基本的栈操作方法。 总之,jmu-java-05集合(泛型)-10-generalstack是一种基于泛型实现的栈,提供了复用和类型安全的目的。其实现使用简便、高效、易于扩展等优点,广泛应用在Java开发中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值