• java的泛型机制详解篇一(基础知识点)


    为什么有泛型

    首先,泛型的这个概念是在java5之后才有的,java5增加泛型支持很大程度上是为了让集合记住其元素的数据类型。在没有泛型之前,一旦把一个对象“丢进”Java 集合,集合就会忘记对象的的类型,把所有的对象当成Object类型处理。当程序从集合中取出对象后,就需要进行强制类型转换,这种强制类型转换不仅使代码臃肿,而且容易引起ClassCastExeception异常

    泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类泛型接口泛型方法

    编译时不检查类型的异常

    泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型),例如下面的代码:
    实例代码:

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: 随风飘的云
     * @describe:编译时不检查类型的异常
     * @date 2022/08/15 18:12
     */
    public class ListErr {
        public static void main(String[] args){
            // 创建一个只想保存字符串的List集合
         	
            List strList = new ArrayList();
            strList.add("abcd");
            strList.add("hhhhh");
            // "不小心"把一个Integer对象"丢进"了集合
            strList.add(5);     // 1
            strList.forEach(str -> System.out.println(((String)str).length())); // 2
    		//如果是下面的写法,那么list中只能放String, 不能放其它类型的元素,
    		//如果添加了其他对象,那么是无法通过编译的。
    		//List list = new ArrayList();
    
    
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    结果: 程序创建了 一个 List 集合 ,而且只希望该 List 集合保存字符串对象,但程序不能进行任何限制,如果程序在①处"不小心"把一个Integer 对象"丢进"了List 集合中,这将导致程序在②处引发ClassCastException 异常,因为程序试图把一个Integer 对象转换为 String 类型。
    在这里插入图片描述

    引入泛型的意义

    在java5中添加了泛型的定义,解决了集合对于对象存储和取出的类型转换问题,除此之外,java5 添加了泛型定义,支持了代码的复用:适用于多种数据类型执行相同的代码

    如果没有java5的泛型支持

    首先,如果没有java5 的泛型支持,那么实现一个简单的加法需要这么多代码。

    /**
     * @author: 随风飘的云
     * @describe:如果没有泛型支持
     * @date 2022/08/15 18:18
     */
    public class TestAlgorith {
    
        private static int add(int a, int b) {
            System.out.println(a + "+" + b + "=" + (a + b));
            return a + b;
        }
    
        private static float add(float a, float b) {
            System.out.println(a + "+" + b + "=" + (a + b));
            return a + b;
        }
    
        private static double add(double a, double b) {
            System.out.println(a + "+" + b + "=" + (a + b));
            return a + b;
        }
    
        public static void main(String[] args) {
            add(1,2);
            add(1.0,2);
            add(1.2, 2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    java5支持的泛型定义

    如果没有泛型,要实现不同类型的加法,每种类型都需要重载一个add方法;通过泛型,我们可以复用为一个方法:

    实例代码:

    /**
     * @author: 随风飘的云
     * @describe:有泛型支持
     * @date 2022/08/15 18:18
     */
    public class TestAlgorith {
        
        private static <T extends Number> double add(T a, T b) {
            System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
            return a.doubleValue() + b.doubleValue();
        }
    
        public static void main(String[] args) {
            add(1,2);
            add(1.0,2);
            add(1.2, 2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    泛型的基本使用

    泛型类

    程序定义了 一个带泛型声明的 Apple< T >类(不要理会这个泛型形参是否具有实际意义) ,使用Apple< T >类时就可为 T 形参传入实际类型,这样就可以生成如 Apple< String > 、 Apple< Double >等等形式的多个逻辑子类(物理上并不存在) 。
    简单泛型类实例代码:

    /**
     * @author: 随风飘的云
     * @describe:泛型类
     * @date 2022/08/15 20:31
     */
    public class Apple<T> { //此处可以随便写标识符号,T是type的简称
        // 使用T类型定义实例变量,即由外部指定
        private T info;
        public Apple(){}
        // 下面方法中使用T类型来定义构造器,设置的值的类型是由外部指定
        public Apple(T info) {
            this.info = info;
        }
        public void setInfo(T info) {
            this.info = info;
        }
        public T getInfo() {
            return this.info;
        }
        public static void main(String[] args) {
            // 由于传给T形参的是String,所以构造器参数只能是String
            Apple<String> a1 = new Apple<>("苹果");
            System.out.println(a1.getInfo());
            // 由于传给T形参的是Double,所以构造器参数只能是Double或double
            Apple<Double> a2 = new Apple<>(5.67);
            System.out.println(a2.getInfo());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    多元泛型类实例代码:

    class Notepad<K,V>{       // 此处指定了两个泛型类型  
        private K key ;     // 此变量的类型由外部决定  
        private V value ;   // 此变量的类型由外部决定  
        public K getKey(){  
            return this.key ;  
        }  
        public V getValue(){  
            return this.value ;  
        }  
        public void setKey(K key){  
            this.key = key ;  
        }  
        public void setValue(V value){  
            this.value = value ;  
        }  
    } 
    public class GenericsDemo09{  
        public static void main(String args[]){  
            Notepad<String,Integer> t = null ;        // 定义两个泛型类型的对象  
            t = new Notepad<String,Integer>() ;       // 里面的key为String,value为Integer  
            t.setKey("汤姆") ;        // 设置第一个内容  
            t.setValue(20) ;            // 设置第二个内容  
            System.out.print("姓名;" + t.getKey()) ;      // 取得信息  
            System.out.print(",年龄;" + t.getValue()) ;       // 取得信息  
      
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    泛型类派生子类

    当创建了带泛型声明的接口、父类之后 ,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口 、父类时不能再包含泛型形参 。 例如:下面的代码是错误的

     //定义类 A 继承 Apple 类, Apple 类不能跟泛型形参
    public class A extends Apple<T>{ }
    
    • 1
    • 2

    如果想从 Apple 类派生一个子类,则可以改为如下代码:

    //使用 Apple 类时为 T 形参传入 String 类型
    public class A extends Apple< String >{}
    
    • 1
    • 2

    调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类、接口时也可以不为泛型形参传入实际的类型参数,即下面代码也是正确的 。

    //使用 Apple 类时,没有为 T 形参传入实际的类型参数
    public class A extends Apple
    
    • 1
    • 2

    如果从 Apple类派生子类 ,则在 Apple 类中所有使用 T 类型的地方都将被替换成 String 类型,即它的子类将会继承到 String getInfo()和 void setlnfo(String info)两个方法,如果子类需要重写父类的方法,就必须注意这一点。

    /**
     * @author: 随风飘的云
     * @describe:泛型类派生子类
     * @date 2022/08/15 20:45
     */
    public class A1 extends Apple<String> {
        // 正确重写了父类的方法,返回值
        // 与父类Apple的返回值完全相同
        public String getInfo() {
            return "子类" + super.getInfo();
        }
    //	// 下面方法是错误的,重写父类方法时返回值类型不一致
    //	public Object getInfo(){
    //		return "子类";
    //	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    如果使用 Apple 类时没有传入实际的类型(即使用原始类型), Java 编译器可能发出警告:使用了未经检查或不安全的操作一这就是泛型检查的警告。如果希望看到该警告提示的更详细信息,则可以通过为 Javac 命令增加-Xlint:unchecked 选项来实现 。 此时,系统会把 Apple类里的 T 形参当成Object 类型处理 。

    /**
     * @author: 随风飘的云
     * @describe:泛型警告
     * @date 2022/08/15 20:48
     */
    public class A2 extends Apple {
        // 重写父类的方法
        public String getInfo() {
            // super.getInfo()方法返回值是Object类型,
            // 所以加toString()才返回String类型
            return super.getInfo().toString();
        }
    
        public static void main(String[] args) {
            Apple<String> a1 = new Apple<>("华为");
            System.out.println(a1.getInfo());
            // 由于传给T形参的是Double,所以构造器参数只能是Double或double
            Apple<Double> a2 = new Apple<>(123.34);
            System.out.println(a2.getInfo());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    泛型接口

    简单的泛型接口:

    interface Info<T>{        // 在接口上定义泛型  
        public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型  
    }  
    class InfoImpl<T> implements Info<T>{   // 定义泛型接口的子类  
        private T var ;             // 定义属性  
        public InfoImpl(T var){     // 通过构造方法设置属性内容  
            this.setVar(var) ;    
        }  
        public void setVar(T var){  
            this.var = var ;  
        }  
        public T getVar(){  
            return this.var ;  
        }  
    } 
    public class GenericsDemo24{  
        public static void main(String arsg[]){  
            Info<String> i = null;        // 声明接口对象  
            i = new InfoImpl<String>("汤姆") ;  // 通过子类实例化对象  
            System.out.println("内容:" + i.getVar()) ;  
        }  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    泛型方法

    泛型方法,是在调用方法的时候指明泛型的具体类型,具体请查看下面的图:(图片参考
    在这里插入图片描述
    调用泛型方法语法格式
    在这里插入图片描述

    1. 说明一下,定义泛型方法时,必须在返回值前边加一个< T >,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。
    2. Class< T >的作用就是指明泛型的具体类型,而Class< T >类型的变量c,可以用来创建泛型类的对象。
    3. 为什么要用变量 c 来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,因此没有办法去new一个对象,但可以利用变量 c 的 newInstance 方法去创建对象,也就是利用反射创建对象。
    4. 泛型方法要求的参数是Class< T >类型,而Class.forName()方法的返回值也是Class< T >,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class< T >就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class< User >类型的对象,因此调用泛型方法时,变量 c 的类型就是Class< User >,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。
    5. 当然,泛型方法不是仅仅可以有一个参数Class,可以根据需要添加其他参数。
    6. 为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。

    类型通配符

    当使用一个泛型类时 (包括声明变量和创建对象两种情况) , 都应该为这个泛型类传入一个类型实参。如果没有传入类型实际参数 , 编译器就会提出泛型警告。假设现在需要定义一个方法 , 该方法里有一个集合形参,集合形参的元素类型是不确定的,考虑下面的代码:

    public void Test(List c){
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
            }
        } 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面的代码定义没有问题,只是一个遍历list集合的代码,问题是上面程序中List 是一个有泛型声明的接口 , 此处使用 List 接口时没有传入实际类型参数,这将引起泛型警告 。 为此,考虑为List 接口传入实际的类型参数一 因为 List 集合里的元素类型是不确定的,将上面的代码改为下面的这种形式:

    public void Test(List<Object> c){
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是又有一个问题,如果我使用这个方法传入的参数是这样子的,那会不会有问题呢?

    //创建一个 List对象
    List<String> strList = new ArrayList <> ();
    //将 strList 作为参数来调用前面的 test 方法
    Test(strList) ; //
    
    • 1
    • 2
    • 3
    • 4

    这当然有问题了,且看实例代码:

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: 随风飘的云
     * @describe:
     * @date 2022/08/15 21:25
     */
    public class test {
        public static void Test(List<Object> c){
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
            }
        }
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("ss");
            Test(list);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    结果:
    在这里插入图片描述
    那该怎么解决这个问题呢?使用类型通配符,什么是类型通配符?类型通配符是一个问号(?) ,将一个问号作为类型实参传给 List 集合,写作: List(意思是元素类型未知的 List ) 。 这个问号(?)被称为通配符,它的元素类型可以匹配任何类型 。 那上面有错误的代码就可以改成这样子的格式了。

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: 随风飘的云
     * @describe:
     * @date 2022/08/15 21:25
     */
    public class test {
        public static void Test(List<?> c){
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
            }
        }
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("ss");
            Test(list);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    泛型的上下限

    当直接使用 List< ?>这种形式时,即表明这个 List 集合可以是任何泛型 List的父类。但还有一种特殊的情形,程序不希望这个 List是任何泛型 List 的父类,只希望它代表某一类泛型 List 的父类。

    泛型的上限

    先来看下面的代码:

    // 定义一个抽象类Shape
    public abstract class Shape {
        public abstract void draw(Canvas c);
    }
    
    • 1
    • 2
    • 3
    • 4
    // 定义Shape的子类Circle
    public class Circle extends Shape {
        // 实现画图方法,以打印字符串来模拟画图方法实现
        public void draw(Canvas c) {
            System.out.println("在画布" + c + "上画一个圆");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // 定义Shape的子类Rectangle
    public class Rectangle extends Shape {
        // 实现画图方法,以打印字符串来模拟画图方法实现
        public void draw(Canvas c) {
            System.out.println("把一个矩形画在画布" + c + "上");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面定义了 三个形状类,其中 Shape 是一个抽象父类 , 该抽象父类有两个子类 : Circle 和 Rectangle 。接下来定义一个 Canvas 类,该画布类可以画数量不等的形状 (Shape 子类的对象) 。那么定义一个Canvas类如下:

    import java.util.ArrayList;
    import java.util.List;
    
    public class Canvas {
    	// 同时在画布上绘制多个形状
    	public void draw(List<Shape> shapes){
    		for (Shape s : shapes){
    			s.draw(this);
    		}
    	}
    
        // 同时在画布上绘制多个形状,使用被限制的泛型通配符
        public void drawAll(List<? extends Shape> shapes) {
            for (Shape s : shapes) {
                s.draw(this);
            }
        }
    
        public static void main(String[] args) {
            List<Circle> circleList = new ArrayList<Circle>();
            Canvas c = new Canvas();
            // 由于List并不是List的子类型,
            // 所以下面代码引发编译错误
            // c.draw(circleList);
            c.drawAll(circleList);
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    需要注意的是上面的 draw()方法的形参类型是 List< Shape > ,而 List< Circle >并不是 List< Shape >的子类型 ,因此,下面代码将引起编译错误。

    List<Circle> circleList = new ArrayList<>( );
    Canvas c = new Canvas ();
    //不能把 List 当成 List使用,所以下面代码引起编译错误
    c.draw(circleList);
    
    • 1
    • 2
    • 3
    • 4

    那有什么方法可以解决呢?有!把 List< Circle >对象当成 List使用。即List可以表示 List< Circle > 、 List< Rectangle >的父类,而且这个Shape类被称之为类型通配符的上限。如上面的drawAll(List shapes)方法代码。

    需要注意的是,由于程序无法确定这个受限制的通配符的具体类型,所以不能把 Shape 对象或其子类的对象加入这个泛型集合中 ,例如下面的代码是错误的:

    public void addRectangle(List<? extends Shape> shapes){
    	//下面代码引起编译错误
    	shapes .add(O , new Rectangle());
    }
    
    • 1
    • 2
    • 3
    • 4

    总的来说,这种指定通配符上限的集合,只能从集合中取元素(取出的元素总是上限的类型) ,不能向集合中添加元素(因为编译器没法确定集合元素实际是哪种子类型) 。

    泛型的下限

    配符的下限用的方式来指定,通配符下限的作用与通配符上限的作用恰好相反 。

    指定通配符的下限就是为了支持类型型变。 比如 Foo 是 Bar 的子类,当程序需要一个 A< ? super Bar >变量时,程序可以将 A< Foo > 、 A< Object >赋值给 A< ? super Bar >类型的变量,这种型变方式被称为逆变 。

    对于逆变的泛型集合来说,编译器只知道集合元素是下限的父类型,但具体是哪种父类型则不确定。因此,这种逆变的泛型集合能向其中添加元素(因为实际赋值的集合元素总是逆变声明的父类) ,从集合中取元素时只能被当成Object 类型处理(编译器无法确定取出的到底是哪个父类的对象) 。例如:

    import java.util.ArrayList;
    import java.util.List;
    
    public class MyUtils {
        // 下面dest集合元素类型必须与src集合元素类型相同,或是其父类
        public static <T> T copy(List<? super T> dest, List<T> src){
            T last = null;
            for (T ele  : src) {
                last = ele;
                // 逆变的泛型集合添加元素是安全的
                dest.add(ele);
            }
            return last;
        }
        public static void main(String[] args) {
            List<Number> ln = new ArrayList<>();
            List<Integer> li = new ArrayList<>();
            li.add(5);
            li.add(12);
            // 此处可准确的知道最后一个被复制的元素是Integer类型
            // 与src集合元素的类型相同
            Integer last = copy(ln , li);    // ①
            System.out.println(ln); // [5,12]
            System.out.println(last); // 12
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    泛型比较

    先再来看一个实例代码:

    private  <E extends Comparable<? super E>> E max(List<? extends E> e1){
        if (e1 == null){
            return null;
        }
        //迭代器返回的元素属于 E 的某个子类型
        Iterator<? extends E> iterator = e1.iterator();
        E result = iterator.next();
        while (iterator.hasNext()){
            E next = iterator.next();
            if (next.compareTo(result) > 0){
                result = next;
            }
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    上述代码中的类型参数 E 的范围是>,我们可以分步查看:

    • 要进行比较,所以 E 需要是可比较的类,因此需要 extends Comparable<…>(注意这里不要和继承的 extends 搞混了,不一样)
    • Comparable< ? super E> 要对E进行比较,即 E 的消费者,所以需要用 super
    • 而参数 List< ? extends E> 表示要操作的数据是 E 的子类的列表,指定上限,这样容器才够大

    如果有多个限制,使用& 解决,如代码所示:

    public class Client {
        //工资低于2500元的上斑族并且站立的乘客车票打8折
        public static <T extends Staff & Passenger> void discount(T t){
            if(t.getSalary()<2500 && t.isStanding()){
                System.out.println("恭喜你!您的车票打八折!");
            }
        }
        public static void main(String[] args) {
            discount(new Me());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    小结

    <?> 无限制通配符
    <? extends E> extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
    <? super E> super 关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
    
    // 使用原则《Effictive Java》
    // 为了获得最大限度的灵活性,要在表示 生产者或者消费者 的输入参数上使用通配符,使用的规则就是:生产者有上限、消费者有下限
    1. 如果参数化类型表示一个 T 的生产者,使用 < ? extends T>;
    2. 如果它表示一个 T 的消费者,就使用 < ? super T>3. 如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    泛型数组

    首先,我们泛型数组相关的申明:

    //编译错误,非法创建 
    List<String>[] list11 = new ArrayList<String>[10]; 
    
    //编译错误,需要强转类型 
    List<String>[] list12 = new ArrayList<?>[10]; 
    
    //OK,但是会有警告 
    List<String>[] list13 = (List<String>[]) new ArrayList<?>[10]; 
    
    //编译错误,非法创建
    List<?>[] list14 = new ArrayList<String>[10];  
    
    //OK 
    List<?>[] list15 = new ArrayList<?>[10]; 
    
    //OK,但是会有警告
    List<String>[] list6 = new ArrayList[10]; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    使用场景:

    public class GenericsDemo30{  
        public static void main(String args[]){  
            Integer i[] = fun1(1,2,3,4,5,6) ;   // 返回泛型数组  
            fun2(i) ;  
        }  
        public static <T> T[] fun1(T...arg){  // 接收可变参数  
            return arg ;            // 返回泛型数组  
        }  
        public static <T> void fun2(T param[]){   // 输出  
            System.out.print("接收泛型数组:") ;  
            for(T t:param){  
                System.out.print(t + "、") ;  
            }  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    文章参考

    • https://www.pdai.tech/md/java/basic/java-basic-x-generic.html
    • https://blog.csdn.net/sunxianghuang/article/details/51982979
    • https://blog.csdn.net/LonelyRoamer/article/details/7868820
    • https://docs.oracle.com/javase/tutorial/extra/generics/index.html
    • https://blog.csdn.net/s10461/article/details/53941091
    • https://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html
    • https://www.cnblogs.com/rudy-laura/articles/3391013.html
    • https://www.jianshu.com/p/986f732ed2f1
    • https://blog.csdn.net/u011240877/article/details/53545041
    • https://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html
  • 相关阅读:
    6.29日刷题题解
    操作系统06-运行机制
    AI基于大模型语言存在的网络安全风险
    C语言--tips1
    01背包(换汤不换药)
    python爬虫入门(五)XPath使用
    【C++编程语言】之面向对象的三大特性之一封装
    RabbitMQ消息可靠性(二)-- 消费者消息确认
    惠普发布最新财报,对AIPC寄予厚望
    ES6 高阶函数的使用详解
  • 原文地址:https://blog.csdn.net/m0_46198325/article/details/126352006