25.泛型

泛型

    • 1.泛型
      • 1.1 概述
      • 1.2 代码示例
    • 2. 泛型类
      • 2.1 概述
      • 2.2 代码示例
    • 3. 泛型方法
      • 3.1 概述
      • 3.2 代码示例
    • 4. 泛型接口
      • 4.1 概述
      • 4.2 代码示例
    • 5. 泛型特点
      • 5.1 概述
      • 5.2 代码示例
    • 6. 泛型通配符
      • 6.1 概述
      • 6.2 代码示例
    • 7. 综合案例
    • 8. 注意事项

1.泛型

泛型是Java编程语言的一项重要功能,它允许开发人员在定义类、接口和方法时使用类型参数。使用泛型可以实现类型的参数化,使代码更加灵活和可重用。

1.1 概述

问题】没有泛型的时候,集合如何存储数据?

结论:如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的,此时可以往集合里添加任意的数据类型(带来一个坏处:我们在获取数据的时候,无法使用它的特有方法),因此产生了泛型,可以在添加数据的时候对数据进行统一,而且在获取数据的时候,也省的强转了。

使用泛型带来了以下几个优势:

  1. 类型安全性:通过使用泛型,编译器可以在编译时检查代码中的数据类型错误,并提供编译时类型安全性。这意味着在编译时就可以检测到类型不匹配的错误,而不是在运行时出现问题。

  2. 代码重用:使用泛型可以编写更为通用的代码,可适用于多种数据类型。这样可以减少代码的冗余,并增加代码的可重用性和维护性。

  3. 更强的类型检查和自动转换:使用泛型可以在编译时进行更强的类型检查,并自动执行类型转换,避免了手动的类型转换。

泛型的好处

  1. 统一数据类型

  2. 把运行时期的问题提前到了编译时期,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确认下来。

泛型的细节

  1. 泛型中不能有基本数据类型

  2. 指定泛型的具体类型后,传递数据时,可以传入该类类型或者子类类型

  3. 如果不写泛型,类型默认是Object

1.2 代码示例

  • 代码示例

    package text.text02;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    /*
    泛型:
    没有泛型的时候,集合如何存储数据
    结论:如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的,此时可以往集合里添加任意的数据类型(带来一个坏处:我们在获取数据的时候,无法使用它的特有方法),因此产生了泛型,可以在添加数据的时候对数据进行统一,而且在获取数据的时候,也省的强转了。
    
    泛型的好处:
        1.统一数据类型
        2.把运行时期的问题提前到了编译时期,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确认下来。
    
    泛型的细节:
        1.泛型中不能有基本数据类型
        2.指定泛型的具体类型后,传递数据时,可以传入该类类型或者子类类型
        3.如果不写泛型,类型默认是Object
     */
    public class text30 {
        public static void main(String[] args) {
            //没有用泛型的方法
            System.out.print("没有用泛型的方法:");
            method1();
    
            System.out.println();
    
            //有用泛型的方法
            System.out.print("有用泛型的方法:");
            method2();
        }
    
        //没有用泛型的方法
        public static void method1() {
            //创建集合对象并添加元素
            ArrayList list = new ArrayList();
            list.add(123);//添加Integer类型
            list.add("aaa");//添加字符串类型
            list.add(true);//添加布尔类型
    
            //通过迭代器遍历集合
            Iterator it = list.iterator();
            while (it.hasNext()) {
                //多态的弊端是不能访问子类的特有方法
                //我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的
                Object o = it.next();
                //o.length(); 采用字符串中Length()方法获取字符串的长度报错 (可以采用强转,但是集合里面有别的类型的,强转比较麻烦)
                System.out.print(o + "  ");        //没有用泛型的方法:123  aaa  true
            }
        }
    
        //有用泛型的方法
        public static void method2() {
            //创建集合并添加元素
            ArrayList<String> list = new ArrayList<>(); //泛型<String>表明该集合只能存入String类型的数据
            list.add("aaa");
            list.add("bbb");
            list.add("ccc");
            list.add("ddd");
    
            //遍历集合并输出数据
            Iterator<String> it = list.iterator();
            while (it.hasNext()) {
                String str = it.next();
                System.out.print(str + "  ");         //有用泛型的方法:aaa  bbb  ccc  ddd
            }
        }
    
    }
    
    
  • 输出结果
    在这里插入图片描述

2. 泛型类

泛型类是使用泛型参数的类。通过在类名后使用尖括号<>包围的类型参数,可以定义泛型类。这样的泛型类可以在类的任何位置使用类型参数作为参数类型或返回类型。

2.1 概述

泛型类:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类

格式:

	    修饰符 class 类名 <类型> {
	
	    }

举例:

public class ArrayList<E>{  //创建该对象时,E就确定了类型
	    //此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型。
	
	        }

2.2 代码示例

要求:自己创建一个带有泛型的ArrayList集合(当在编写一个类的时候,如果不能确认类型,那么这个类可以定义为泛型类)

  • 代码示例

    package text.text02;
    
    import java.util.Arrays;
    
    /*
    泛型类:
        当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
    
    格式:
        修饰符 class 类名 <类型> {
    
        }
    
    要求:自己创建一个带有泛型的ArrayList集合(当在编写一个类的时候,如果不能确认类型,那么这个类可以定义为泛型类)
     */
    public class text31 {
        public static void main(String[] args) {
            //创建MyArrayList的集合对象并添加数据
            MyArrayList<String> list = new MyArrayList<>();
            list.add("aaa");
            list.add("ddd");
            list.add("ccc");
            System.out.println(list);      //[aaa, ddd, ccc, null, null, null, null, null, null, null]
    
            //获取索引为1的元素
            String str = list.get(1);
            System.out.println("索引为1的元素:" + str);          //索引为1的元素:ddd
        }
    }
    
    class MyArrayList<E> {
        //创建一个长度为10的数组
        Object[] arr = new Object[10];
        //定义变量记录存储数据的长度,初始化为0
        int size;
    
        /*
        E:表示是不确定的类型,该类型在类名后面已经定义过了
        e:形参的名字,变量名
         */
        //添加数据的方法
        public boolean add(E e) {
            arr[size] = e;
            size++;
            return true;
        }
    
        //获取数据的方法
        public E get(int index) {
            return (E) arr[index];
        }
    
        //重写toString方法
        @Override
        public String toString() {
            return Arrays.toString(arr);
        }
    }
    
  • 输出结果
    在这里插入图片描述

3. 泛型方法

泛型方法是在方法定义中使用泛型参数的方法。通过在方法名前使用尖括号<>包围的类型参数,可以定义泛型方法。这样的泛型方法可以在方法的参数类型、返回类型或方法体中使用类型参数。

3.1 概述

泛型方法:

  1. 使用类名后面定义的类型(所有的方法都能使用)

  2. 在方法申明上定义自己的泛型(只有本方法能用)

格式:

 修饰符 <类型> 返回值类型 方法名 (类型  变量名){
   
 }

例如:

public <T> void add(T t){
	    //此处T可以理解为变量,但是不是用来记录数据的,而是记录数据的类型。
	
}

3.2 代码示例

要求:定义一个工具类:ListUtil,类中定义一个静态方法addAll,用来将多个元素添加进集合。

  • 代码示例

    package text.text02;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    
    /*
    泛型方法:
        1.使用类名后面定义的类型(所有的方法都能使用)
        2.在方法申明上定义自己的泛型(只有本方法能用)
    
    格式:
        修饰符 <类型> 返回值类型 方法名 (类型  变量名){
    
        }
    
    要求:定义一个工具类:ListUtil,类中定义一个静态方法addAll,用来将多个元素添加进集合。
     */
    public class text32 {
        public static void main(String[] args) {
            //创建集合对象
            ArrayList<String> list = new ArrayList<>();
    
            //调用addAll方法
            ListUtil.addAll(list, "aaa", "bbb", "ccc", "ddd", "eee", "fff");
    
            System.out.println(list);         //[aaa, bbb, ccc, ddd, eee, fff]
        }
    
    }
    
    //工具类:ListUtil
    class ListUtil {
        //私有化构造方法
        private ListUtil() {
        }
    
        //在方法申明上定义自己的泛型(只有本方法能用) 
        public static <T> void addAll(ArrayList<T> list, T t1, T t2, T t3, T t4, T t5, T t6) {   //添加多个元素:T... t(底层是一个可变数组))
            list.add(t1);
            list.add(t2);
            list.add(t3);
            list.add(t4);
            list.add(t5);
            list.add(t6);
        }
    }
    
    
  • 输出结果
    在这里插入图片描述

4. 泛型接口

泛型接口是在接口定义中使用泛型参数的接口。通过在接口名称后面使用尖括号<>包围的类型参数,可以定义泛型接口。这样的泛型接口可以在接口的方法参数类型、返回类型或方法体中使用类型参数。

4.1 概述

泛型接口:
定义一个接口时,数据类型不确定时,就可以定义带有泛型的接口。

格式:

修饰符 interface 接口名 <类型>{
	
	    }

例如:

 public interface  List<E>{
	    //此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型。
	
 }

泛型接口的使用方法:

  • 方法1:实现类给出具体的类型

  • 方法2:实现类延续泛型,创建对象时再确定

4.2 代码示例

  • 代码示例

    package text.text02;
    
    import java.util.*;
    
    /*
    泛型接口:
        定义一个接口时,数据类型不确定时,就可以定义带有泛型的接口。
    
    格式:修饰符 interface 接口名 <类型>{
    
        }
    
    泛型接口的使用方法:
    方法1:实现类给出具体的类型
    方法2:实现类延续泛型,创建对象时再确定
     */
    public class text33 {
        public static void main(String[] args) {
            //方法1:实现类给出具体的类型,创建对象时不用确认数据类型
            MyArrayList1 list1 = new MyArrayList1();
            list1.add("aaa");
            list1.add("bbb");
            list1.add("ccc");
            //list1.add(123);由于在实现类中确认的是字符串类型的,因此添加Integer类型的会报错
            System.out.print("方法1:实现类给出具体的类型,创建对象时不用确认数据类型:");
            for (int i = 0; i < list1.size(); i++) {
                System.out.print(list1.get(i) + "  ");           //aaa  bbb  ccc
            }
    
            System.out.println();
    
            //方法2:实现类延续泛型,创建对象时再确定
            MyArrayList2<String> list2 = new MyArrayList2<>();
            list2.add("ddd");
            list2.add("eee");
            list2.add("fff");
            //list2.add(123);由于在创建对象时确定了是字符串类型的,因此添加Integer类型的会报错
            System.out.print("方法2:实现类延续泛型,创建对象时再确定:");
            for (int i = 0; i < list2.size(); i++) {
                System.out.print(list2.get(i) + "  ");           //ddd  eee  fff
            }
        }
    }
    
    //方法1:实现类给出具体的类型
    class MyArrayList1 implements List<String> {     //List是一个Java已经定义好的为泛型的接口 :public interface List<E> extends Collection<E>
        Object[] arr = new Object[10];
        int size;
    
        @Override
        public int size() {
            return size;
        }
    
        @Override
        public boolean isEmpty() {
            return false;
        }
    
        @Override
        public boolean contains(Object o) {
            return false;
        }
    
        @Override
        public Iterator<String> iterator() {
            return null;
        }
    
        @Override
        public Object[] toArray() {
            return new Object[0];
        }
    
        @Override
        public <T> T[] toArray(T[] a) {
            return null;
        }
    
        @Override
        public boolean add(String s) {
            arr[size] = s;
            size++;
            return true;
        }
    
        @Override
        public boolean remove(Object o) {
            return false;
        }
    
        @Override
        public boolean containsAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean addAll(Collection<? extends String> c) {
            return false;
        }
    
        @Override
        public boolean addAll(int index, Collection<? extends String> c) {
            return false;
        }
    
        @Override
        public boolean removeAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean retainAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public void clear() {
    
        }
    
        @Override
        public String get(int index) {
            return (String) arr[index];
        }
    
        @Override
        public String set(int index, String element) {
            return null;
        }
    
        @Override
        public void add(int index, String element) {
    
        }
    
        @Override
        public String remove(int index) {
            return null;
        }
    
        @Override
        public int indexOf(Object o) {
            return 0;
        }
    
        @Override
        public int lastIndexOf(Object o) {
            return 0;
        }
    
        @Override
        public ListIterator<String> listIterator() {
            return null;
        }
    
        @Override
        public ListIterator<String> listIterator(int index) {
            return null;
        }
    
        @Override
        public List<String> subList(int fromIndex, int toIndex) {
            return null;
        }
    
    
    }
    
    //方法2:实现类延续泛型,创建对象时再确定
    class MyArrayList2<E> implements List<E> {
        Object[] arr = new Object[10];
        int size;
    
        @Override
        public int size() {
            return size;
        }
    
        @Override
        public boolean isEmpty() {
            return false;
        }
    
        @Override
        public boolean contains(Object o) {
            return false;
        }
    
        @Override
        public Iterator<E> iterator() {
            return null;
        }
    
        @Override
        public Object[] toArray() {
            return new Object[0];
        }
    
        @Override
        public <T> T[] toArray(T[] a) {
            return null;
        }
    
        @Override
        public boolean add(E s) {
            arr[size] = s;
            size++;
            return true;
        }
    
        @Override
        public boolean remove(Object o) {
            return false;
        }
    
        @Override
        public boolean containsAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean addAll(Collection<? extends E> c) {
            return false;
        }
    
        @Override
        public boolean addAll(int index, Collection<? extends E> c) {
            return false;
        }
    
        @Override
        public boolean removeAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean retainAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public void clear() {
    
        }
    
        @Override
        public E get(int index) {
            return (E) arr[index];
        }
    
        @Override
        public E set(int index, E element) {
            return null;
        }
    
        @Override
        public void add(int index, E element) {
    
        }
    
        @Override
        public E remove(int index) {
            return null;
        }
    
        @Override
        public int indexOf(Object o) {
            return 0;
        }
    
        @Override
        public int lastIndexOf(Object o) {
            return 0;
        }
    
        @Override
        public ListIterator<E> listIterator() {
            return null;
        }
    
        @Override
        public ListIterator<E> listIterator(int index) {
            return null;
        }
    
        @Override
        public List<E> subList(int fromIndex, int toIndex) {
            return null;
        }     //List是一个Java已经定义好的为泛型的接口 :public interface List<E> extends Collection<E>
    
    }
    
  • 输出结果
    在这里插入图片描述

5. 泛型特点

5.1 概述

泛型是一种参数化类型的机制,它在编程语言中引入了类型参数,使得代码可以更加通用和类型安全。

泛型不具备继承关系,但是数据可以继承

5.2 代码示例

package text.text02;

import java.util.ArrayList;

/*
泛型不具备继承关系,但是数据可以继承
*/
public class text34 {
    public static void main(String[] args) {

    }

    //泛型不具备继承关系,但是数据可以继承
    public static void method1() {
        //创建集合对象
        ArrayList<Ye1> list1 = new ArrayList<>();
        ArrayList<Fu1> list2 = new ArrayList<>();
        ArrayList<Zi1> list3 = new ArrayList<>();

        //泛型不具备继承关系
        //调用method2方法,将创建的集合对象作为参数传进去
        method2(list1);
        //method2(list2);代码报错,说明泛型不具备继承关系
        //method2(list3);代码报错,说明泛型不具备继承关系

        //泛型数据可以继承
        list1.add(new Ye1());
        list1.add(new Fu1());
        list1.add(new Zi1());
    }

    //定义一个方法,只能接受Fu类类型的数据
    public static void method2(ArrayList<Ye1> list) {

    }
}


//定义一个继承关系:Ye1 Fu1 Zi1
class Ye1 {
}

class Fu1 extends Ye1 {
}

class Zi1 extends Fu1 {
}

6. 泛型通配符

泛型通配符(Wildcard)是 Java 泛型中的一个特性,用于在泛型类、接口和方法中表示一种未知类型。它使用问号(?)来表示。通配符允许你使用不具体的类型参数,从而增加代码的灵活性和通用性。

6.1 概述

泛型的通配符:? (限定类型的范围)

  • ? extends E : 表示可以传递E或者E的子类类型

上界通配符:使用上界通配符? extends 类型表示未知类型是给定类型的子类型或本身。它限制了类型的上界,表示类型必须是某个具体类型或其子类型。通常用于限制方法的参数类型或方法的返回类型。

  • ? super E :表示可以传递E或者E的父类类型

下界通配符:使用下界通配符? super 类型表示未知类型是给定类型的父类型或本身。它限制了类型的下界,表示类型必须是某个具体类型或其父类型。通常用于限制方法的参数类型。

泛型通配符的主要特点:

  • 灵活性:通配符允许你使用一种未知的类型,从而使得代码可以处理不同的类型参数。

  • 通用性:通配符可以适用于任何类型,使得代码更加通用和可重用。

  • 安全性:泛型通配符提供了类型安全性,编译器会检查通配符使用的合法性,并在编译时发出警告或错误。

应用场景:

  1. 如果我们在定义类,方法,接口的时候,如果类型不确定,就可以定义泛型类,泛型方法,泛型接口

  2. 如果类型不确定,但是能知道以后只能传递某个继承体系的,就可以使用泛型的通配符

6.2 代码示例

需求:定义一个方法,形参是一个集合,但是集合中的数据类型不能确定

package text.text02;
/*
泛型的通配符:?  (限定类型的范围)
    ? extends E : 表示可以传递E或者E的子类类型
    ? super E :表示可以传递E或者E的父类类型

 */

import java.util.ArrayList;

public class text35 {
    public static void main(String[] args) {
        //创建集合并调用method系列方法
        ArrayList<Ye2> list1 = new ArrayList<>();
        ArrayList<Fu2> list2 = new ArrayList<>();
        ArrayList<Zi2> list3 = new ArrayList<>();
        ArrayList<Student> list4 = new ArrayList<>();

        //利用泛型方法有个弊端,此时method1方法可以接受任意的数据类型
        method1(list1);
        method1(list2);
        method1(list3);
        method1(list4);   //list4是学生类型的,没有继承关系,但是依旧可以调用method1方法


        //利用泛型通配符的方式一:? extends E : 表示可以传递E或者E的子类类型
        method2(list1);
        method2(list2);
        method2(list3);
        //method2(list4);代码报错,因为list4是学生类型的,没有继承关系,因此不能调用method2方法

        //利用泛型通配符的方式二:? super E :表示可以传递E或者E的父类类型
        method3(list1);
        method3(list2);
        method3(list3);
        //method3(list4);代码报错,因为list4是学生类型的,没有继承关系,因此不能调用method3方法
    }

    //可以利用泛型方法的方式,但是利用泛型方法有个弊端,此时method1方法可以接受任意的数据类型
    public static <E> void method1(ArrayList<E> list) {

    }

    //利用泛型通配符的方式
    //? extends E : 表示可以传递E或者E的子类类型
    //? super E :表示可以传递E或者E的父类类型
    public static void method2(ArrayList<? extends Ye2> list) {  //说明该方法只能传递Ye2数据类型的或者继承Ye2数据类型的list对象

    }

    public static void method3(ArrayList<? super Zi2> list) {  //说明该方法只能传递Zi2数据类型的或者Zi2数据类型的父类的list对象

    }
}

//定义一个继承关系:Ye2 Fu2 Zi2
class Ye2 {
}

class Fu2 extends Ye2 {
}

class Zi2 extends Fu2 {
}

//再定义一个没有继承结构的学生类
class Student {
}

7. 综合案例

  • 需求:

      定义一个继承结构:
                                        动物
         					    |                 |
                                 猫                狗
                          |           |        |        |
                       波斯猫        狸花猫     泰迪    哈士奇
    
     属性:名字,年龄
     行为:吃东西
          方法体打印:一只叫做XXX的,X岁的波斯猫,正在吃小饼干
          方法体打印:一只叫做XXX的,X岁的狸花猫,正在吃鱼
          方法体打印:一只叫做XXX的,X岁的泰迪,正在吃骨头,边吃边蹭
          方法体打印:一只叫做XXX的,X岁的哈士奇,正在吃骨头,边吃边拆家
    
     测试类中定义一个方法,用来饲养动物
     public static void keepPet(ArrayList<???> list){
      //遍历集合,调用动物的eat方法
     }
    
     要求1:该方法能养所有品种的猫,但是不能养狗
     要求2:该方法能养所有品种的狗,但是不能养猫
     要求3:该方法能养所有品种的动物,但是不能传递其他类型
    
  • 代码实现:

    • 组父类:Animal类
    package code.code36;
    
    public abstract class Animal {
        private String name;
        private int age;
    
        public Animal() {
        }
    
        public Animal(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        /**
         * 获取
         *
         * @return name
         */
        public String getName() {
            return name;
        }
    
        /**
         * 设置
         *
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * 获取
         *
         * @return age
         */
        public int getAge() {
            return age;
        }
    
        /**
         * 设置
         *
         * @param age
         */
        public void setAge(int age) {
            this.age = age;
        }
    
        public String toString() {
            return "Animal{name = " + name + ", age = " + age + "}";
        }
    
        public abstract  void eat();
    }
    
    
    • 父类:Cat类
    package code.code36;
    
    public abstract class Cat extends Animal {
    
        public Cat() {
        }
    
        public Cat(String name, int age) {
            super(name, age);
        }
    
    }
    
    
    • 父类:Dog类
    package code.code36;
    
    public abstract class Dog extends Animal {
    
        public Dog() {
        }
    
        public Dog(String name, int age) {
            super(name, age);
        }
    
    }
    
    
    • 子类:波斯猫类
    package code.code36;
    
    //波斯猫
    public class PersianCat extends Cat {
        public PersianCat() {
        }
    
        public PersianCat(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的波斯猫,正在吃小饼干");
        }
    }
    
    
    • 子类:狸花猫类
     package code.code36;
    
    //狸花猫
    public class LiHuaCat extends Cat {
        public LiHuaCat() {
        }
    
        public LiHuaCat(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的狸花猫,正在吃鱼");
        }
    
    }
    
    
    • 子类:哈士奇类
    package code.code36;
    
    //哈士奇
    public class HaShiQi extends Dog {
        public HaShiQi() {
        }
    
        public HaShiQi(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的哈士奇,正在吃骨头,边吃边拆家");
        }
    }
    
    
    • 子类:泰迪类
    package code.code36;
    
    //泰迪
    public class TaiDi extends Dog {
        public TaiDi() {
        }
    
        public TaiDi(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的泰迪,正在吃骨头,边吃边蹭");
        }
    
    }
    
    
    • 测试类:
    package code.code36;
    
    import java.util.ArrayList;
    
    public class CodeText36 {
       public static <E> void main(String[] args) {
           //创建动物的集合对象,并将动物对象添加进集合
           ArrayList<PersianCat> list1 = new ArrayList<>();
           ArrayList<LiHuaCat> list2 = new ArrayList<>();
           ArrayList<TaiDi> list3 = new ArrayList<>();
           ArrayList<HaShiQi> list4 = new ArrayList<>();
    
           list1.add(new PersianCat("波斯", 2));
           list2.add(new LiHuaCat("狸花", 3));
           list3.add(new TaiDi("泰迪", 4));
           list4.add(new HaShiQi("二哈", 5));
    
           //要求1:该方法能养所有品种的猫,但是不能养狗
           System.out.println("要求1:该方法能养所有品种的猫,但是不能养狗:");
           keepPet1(list1);         //一只叫做波斯的,2岁的波斯猫,正在吃小饼干
           keepPet1(list2);         //一只叫做狸花的,3岁的狸花猫,正在吃鱼
    
           System.out.println("===============================");
    
           //要求2:该方法能养所有品种的狗,但是不能养猫
           System.out.println("要求2:该方法能养所有品种的狗,但是不能养猫:");
           keepPet2(list3);         //一只叫做泰迪的,4岁的泰迪,正在吃骨头,边吃边蹭
           keepPet2(list4);         //一只叫做二哈的,5岁的哈士奇,正在吃骨头,边吃边拆家
    
           System.out.println("===============================");
    
           //要求3:该方法能养所有品种的动物,但是不能传递其他类型
           System.out.println("要求3:该方法能养所有品种的动物,但是不能传递其他类型:");
           keepPet3(list1);        //一只叫做波斯的,2岁的波斯猫,正在吃小饼干
           keepPet3(list2);        //一只叫做狸花的,3岁的狸花猫,正在吃鱼
           keepPet3(list3);        //一只叫做泰迪的,4岁的泰迪,正在吃骨头,边吃边蹭
           keepPet3(list4);        //一只叫做二哈的,5岁的哈士奇,正在吃骨头,边吃边拆家
       }
    
       //要求1:该方法能养所有品种的猫,但是不能养狗
       public static void keepPet1(ArrayList<? extends Cat> list) {
           //遍历集合,调用动物的eat方法
           for (Cat cat : list) {
               cat.eat();
           }
       }
    
       //要求2:该方法能养所有品种的狗,但是不能养猫
       public static void keepPet2(ArrayList<? extends Dog> list) {
           //遍历集合,调用动物的eat方法
           for (Dog dog : list) {
               dog.eat();
           }
       }
    
       //要求3:该方法能养所有品种的动物,但是不能传递其他类型
       public static void keepPet3(ArrayList<? extends Animal> list) {
           //遍历集合,调用动物的eat方法
           for (Animal animal : list) {
               animal.eat();
           }
       }
    }
    
    
    • 输出结果:
      在这里插入图片描述

8. 注意事项

  1. 参数化类型不能使用基本数据类型

泛型的类型参数只能是引用类型,不能是基本数据类型。如果需要使用基本数据类型,可以使用对应的包装类(如Integer代替int)。

  1. 无法创建具体的泛型类型的实例

不能直接使用泛型类型来创建对象,例如List<String> list = new List<String>();是错误的。可以使用原始类型(如List)或通配符来创建对象,例如List<?> list = new ArrayList<>();是正确的。

  1. 泛型类型的类型参数在运行时被擦除

泛型在编译时进行类型检查,但在运行时会被擦除为原始类型。这意味着在运行时无法获得泛型类型的具体参数类型。例如,对于List<String>List<Integer>,在运行时它们都被视为List<Object>

  1. 泛型数组的创建受限

无法直接创建带有泛型类型参数的数组,例如List<String>[] array = new List<String>[10];是错误的。可以使用通配符或原始类型数组,例如List<?>[] array = new List<?>[10];是正确的。

  1. 不能使用基本类型作为类型参数

泛型类型参数不能是基本类型,只能是引用类型。如果需要使用基本类型作为类型参数,可以使用对应的包装类。

  1. 泛型类型的类型参数不能是基本类型的数组

泛型类型的类型参数不能是基本类型的数组,例如List<int[]>是错误的。

  1. 类型擦除可能导致运行时异常

由于泛型的类型参数在运行时被擦除,可能会导致在运行时出现类型相关的异常。因此,在使用泛型时要格外小心,并进行必要的类型检查和转换。

  1. 通配符的使用限制

使用通配符时,不能使用null以外的具体值,只能用于方法声明、参数传递和通配符类型的赋值操作。

  1. 泛型类型的兼容性

泛型类型在类型参数相同的情况下才具有兼容性。例如,List<String>List<Integer>不是兼容类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/366691.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

故障诊断 | 一文解决,CNN-LSTM卷积神经网络-长短期记忆神经网络组合模型的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,CNN-LSTM卷积神经网络-长短期记忆神经网络组合模型的故障诊断(Matlab) 模型描述 CNN-LSTM模型是一种结合了卷积神经网络(Convolutional Neural Network)和长短期记忆神经网络(Long Short-Term Memory)的组合模型,常用于数据故障…

FPGA解码MIPI视频:Xilinx Artix7-35T低端FPGA,基于MIPI CSI-2 RX Subsystem架构实现,提供工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐我这里已有的 MIPI 编解码方案本方案在Xilinx Artix7-100T上解码MIPI视频的应用本方案在Xilinx Kintex7上解码MIPI视频的应用本方案在Xilinx Zynq7000上解码MIPI视频的应用本方案在Xilinx Zynq UltraScale上解码MIPI视频的应用纯VHDL代码解…

vite和vue-cli实现原理和优化及区别

Vite&#xff1a; 1. 实现原理&#xff1a; Vite 是一个基于 ESModule 的构建工具。它利用原生 ESModule 的特性&#xff0c;将每个文件作为一个模块&#xff0c;通过浏览器去解析和执行&#xff0c;而不需要提前将文件打包成一个单独的 bundle。Vite 利用浏览器的原生 ESMod…

适用于汽车 4D 成像雷达的双器件毫米波级联参考设计(TI文档)

说明 该汽车雷达参考设计是一个 76GHz 至 81GHz 的级联雷达传感器模块。这包括由 AWR2243 器件和AM2732R 雷达处理器构成的双器件级联阵列。在这一级联雷达配置中&#xff0c;一个主器件向主器件和辅助器件分配20GHz 的本机振荡器 (LO) 信号&#xff0c;使这两个器件作为单个射…

Windows Server 2019 Web服务器搭建

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

idea配置tomcat

推荐链接&#xff1a;IntelliJ IDEA中配置Tomcat&#xff08;超详细&#xff09;_idea怎么配置tomcat服务器-CSDN博客 1,官员下载链接&#xff1a;Apache Tomcat - Welcome! 附本人下载的 tomcat9 的百度网盘链接 链接&#xff1a;https://pan.baidu.com/s/1DpyBGnG4mUGTm5Z…

AI-数学-高中-18-三角函数-同角三角函数关系及计算

原作者视频&#xff1a;三角函数】5同角三角函数关系&#xff08;易中档&#xff09;_哔哩哔哩_bilibili 辅助三角形&#xff08;计算速度快&#xff09;&#xff1a;1.画一个辅助计算的任意直接三角形&#xff1b;2.利用初中方法先计算sin、cos、tan值&#xff1b;3.看象限确定…

【每日一题】石子游戏 VI

文章目录 Tag题目来源解题思路方法一&#xff1a;贪心排序 写在最后 Tag 【贪心排序】【数组】【2024-02-02】 题目来源 1686. 石子游戏 VI 解题思路 方法一&#xff1a;贪心排序 思路 假设有两个石子 i 和 j&#xff0c;Alice 和 Bob 认为它们的价值分别为 a i a_i ai​…

加速知识检索:伯克利DeepMind联合研究,RaLMSpec让语言模型服务飞速提升2-7倍!

近年来&#xff0c;随着大型语言模型&#xff08;LLM&#xff09;的出现&#xff0c;在多样化的 NLP 任务上取得了令人瞩目的成果。然而&#xff0c;知识密集型任务仍是 NLP 领域中的一项挑战&#xff0c;因为这些任务不仅要求模型要理解和生成自然语言&#xff0c;还要能够访问…

springboot150基于springboot的贸易行业crm系统

基于springboot的贸易行业crm系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于springboot的贸易行业crm系统的开发全过程。通过分析基于springboot的贸易行业crm系统管理的不足&#xff0c;创建了一个…

根据天数计算年、日期计算

根据具体天数计算共多少年多少月多少天 效果如图&#xff1a; <input type"text" id"inputDays" placeholder"输入天数"><button id"calculateButton">计算</button><div id"result"></div>…

【Java程序设计】【C00207】基于(JavaWeb+SSM)的宠物领养管理系统(论文+PPT)

基于&#xff08;JavaWebSSM&#xff09;的宠物领养管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的宠物领养系统 本系统分为前台系统、管理员、收养者和寄养者4个功能模块。 前台系统&#xff1a;游客打开系统…

安卓SurfaceTexture中updateTexImage使用及源码分析

文章目录 引言updateTexImage 简单使用SurfaceTexture 初始化相关源码分析Surface 绘制流程源码分析createBufferQueue 源码分析SurfaceTexture 之 updateTexImage 源码分析结尾 本文首发地址 https://h89.cn/archives/140.html 最新更新地址 https://gitee.com/chenjim/chenji…

Android 横屏应用开发如何隐藏左边黑色边缘

最近公司开发一个横屏应用的项目&#xff0c;Phone和Pad一套代码编译&#xff0c;需要考虑到全局横屏状态下的应用&#xff0c;起初竖屏的时候代码是没问题的&#xff0c;可是到切换横屏遇到了黑边问题&#xff0c;先来看看竖屏的时候怎么写的 setContentView之前设置 getWind…

【CSS】外边距折叠(margin 塌陷)

外边距折叠(collapsing margins) 毗邻的两个或多个margin会合并成一个margin&#xff0c;叫做外边距折叠。 规则如下: 两个或多个毗邻的普通流中的块元素垂直方向上的 margin会折叠浮动元素 / inline-block元素 / 绝对定位元素 / 行内元素的margin不会和垂直方向上的其他元素…

Next.js初识

Next.js初识 Next.js&#xff1a;这是一个用于生产环境的React 框架&#xff08;国外用的比较多&#xff09;。 Next.js 为您提供生产环境所需的所有功能以及最佳的开发体验&#xff1a;包括静态及服务器端融合渲染、 支持 TypeScript、智能化打包、 路由预取等功能 无需任何配…

2024年美国大学生数学建模比赛MCM问题A:资源可用性和性别比例-思路解析与代码解答

2024 MCM Problem A: Resource Availability and Sex Ratios 一、题目翻译 背景 虽然一些动物物种存在于通常的雄性或雌性性别之外&#xff0c;但大多数物种实质上是雄性或雌性。虽然许多物种在出生时的性别比例为1&#xff1a;1&#xff0c;但其他物种的性别比例并不均匀。…

VR视频编辑解决方案,全新视频内容创作方式

随着科技的飞速发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术正逐渐成为各个领域的创新力量。而美摄科技&#xff0c;作为VR技术的引领者&#xff0c;特别推出了一套全新的VR视频编辑方案&#xff0c;为企业提供了一个全新的视频内容创作方式。 美摄科技的VR视频编…

八、硬盘(disk)

永久存储性设备&#xff1a;电脑硬盘、移动硬盘、U盘、机身内存… 世界第一块硬盘叫做350RAMAC&#xff0c;是1956年诞生的&#xff0c;IBM公司生产的&#xff0c;比冰箱大&#xff0c;1吨重&#xff0c;只有5MB的容量 1、机械硬盘(HDD) 机械硬盘(Hard Disk Drive&#xff0c;简…

2024年美赛数学建模A题思路分析 - 资源可用性和性别比例

# 1 赛题 问题A&#xff1a;资源可用性和性别比例 虽然一些动物物种存在于通常的雄性或雌性性别之外&#xff0c;但大多数物种实质上是雄性或雌性。虽然许多物种在出生时的性别比例为1&#xff1a;1&#xff0c;但其他物种的性别比例并不均匀。这被称为适应性性别比例的变化。…