zoukankan      html  css  js  c++  java
  • 10_Java泛型

    本章章节

    > 10.1为什么需要泛型
    > 10.2泛型类
    > 10.3 泛型接口
    > 10.4泛型方法
    > 10.5用泛型定义数组
    > 10.6建立类型为泛型类的数组
    > 10.7泛型类充当泛型的实例化类
    > 10.8通配符

      Java泛型(generics)是JDK 5中引入的一个新特性,它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在JDK 5中的新集合类框架中。

    10.1 为什么需要泛型

      现在我要设计一个可以表示坐标的点类,坐标由xy组成。可是不同的人,可能想要知道的坐标不一样。有人想要坐标用整型表示;有人想要坐标用小数表示;有有人想要坐标用字符串表示,如下:

    A这个人想要看到整型的坐标,例如:(10, 20)。所以我们要定义一个IntPoint类如下:

    //IntPoint.java

    public class IntPoint {
      private int x;
      private int y;
    
      public IntPoint(){
      }
    
      public IntPoint(int x, int y) {
        this.x = x;
        this.y = y;
      }
    
      public int getX() {
        return x;
      }
    
      public void setX(int x) {
        this.x = x;
      }
    
      public int getY() {
        return y;
      }
    
      public void setY(int y) {
        this.y = y;
      }
    }

    B这个人想要看到小数的坐标,例如(10.5, 20.6)。所以我们要定义一个DoublePoint类如下:

    //DoublePoint.java

    public class DoublePoint {
      private double x;
      private double y;
    
      public DoublePoint(){
      }
    
      public DoublePoint(double x, double y) {  
        this.x = x;
        this.y = y;
      }
    
      public double getX() {
        return x;
      }
    
      public void setX(double x) {
        this.x = x;
      }
    
      public double getY() {
        return y;
      }
    
      public void setY(double y) {
        this.y = y;
      }
    }

    C这个人想要看到字符串的坐标。例如:("东经180", y="北纬120"),所以我们要定义一个StringPoint类如下:

    //StringPoint.java

    public class StringPoint {
      private String x;
      private String y;
    
      public StringPoint(){
      }
    
      public StringPoint(String x, String y) {
        this.x = x;
        this.y = y;
      }
    
      public String getX() {
        return x;
      }
    
      public void setX(String x) {
        this.x = x;
      }
    
      public String getY() {
        return y;
      }
    
      public void setY(String y) {
        this.y = y;
      }
    }

    使用过程如下:

    //TestPoint.java

    public class TestPoint {
      public static void main(String[] args) {
        IntPoint ptInt = new IntPoint(10, 20);
        System.out.println("(" + ptInt.getX() + "," + ptInt.getY() + ")");
    
        DoublePoint ptDouble = new DoublePoint(10.5, 20.6);
        System.out.println("(" + ptDouble.getX() + "," + ptDouble.getY() + ")");
    
        StringPoint ptString = new StringPoint("东经180度", "北纬120度");
        System.out.println("(" + ptString.getX() + "," + ptString.getY() + ")");
      }
    }

      我们发现其实上面的三个类的基本形式都是一模一样的,除了表示坐标的xy数据类型不一样而已。这样写出的代码冗余度太高,而且维护和扩展比较麻烦。比如我现在想要增加一个打印坐标的功能,我不得不在每一个坐标类都增加一个disp函数。那么有没有什么方法能帮我们做到只定义一个点类,就可以表示所有的可能呢?

    第一种解决方法是利用Object类,因为Object是所有类的父类,所以将点类声明如下即可:

    //ObjectPoint.java

    public class ObjectPoint
    {
        private Object x;
        private Object y;
    
        public ObjectPoint()
        {
        }
    
        public ObjectPoint(Object x, Object y)
        {
            this.x = x;
            this.y = y;
        }
    
        public Object getX()
        {
            return x;
        }
    
        public void setX(Object x)
        {
            this.x = x;
        }
    
        public Object getY()
        {
            return y;
        }
    
        public void setY(Object y)
        {
            this.y = y;
        }
    }

    使用过程如下:

    //TestPoint.java

    public class TestPoint
    {
        public static void main(String[] args)
        {
            ObjectPoint ptObj1 = new ObjectPoint(new Integer(10), new Integer(20));
         //或ObjectPoint ptObj1 = new ObjectPoint(10, 20);
    
            int iV1 = Integer.parseInt(ptObj1.getX().toString());
         //或采用int iV1 = (Integer)(ptObj1.getX());强制类型转换为Integer后再自动拆箱
    
            int iV2 = Integer.parseInt(ptObj1.getY().toString());
            System.out.println("(" + iV1 + "," + iV2 + ")");
    
            ObjectPoint ptObj2 = new ObjectPoint(new Double(10.5), new Double(20.6));
         //或ObjectPoint ptObj2 = new ObjectPoint(10.5, 20.6);
    
            double dV1 = Double.parseDouble(ptObj2.getX().toString());
            double dV2 = Double.parseDouble(ptObj2.getY().toString());
    
            System.out.println("(" + dV1 + "," + dV2 + ")");
    
            ObjectPoint ptObj3 = new ObjectPoint(new String("东经180度"), new String("北纬120度"));
         //或ObjectPoint ptObj3 = new ObjectPoint("东经180度", "北纬120度");
    
            String sV1 = ptObj3.getX().toString();
            String sV2 = ptObj3.getY().toString();
            System.out.println("(" + sV1 + "," + sV2 + ")");
        }
    }

      在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。比如写ObjectPoint ptObj1 = new ObjectPoint(10, 20);的时候,容易造成一些隐藏的问题,一不小心写成:ObjectPoint ptObj1 = new ObjectPoint(10.3, 20);编译的时候不报错,但是运行的时候就会有类型不匹配问题。所以Object是一种弱类型检测,执行的时候才知道具体的数据类型,所以不安全。

      第二种方法是利用泛型(Generics),泛型的目的在于对参数类型本身进行参数化。它属于强类型检测(在使用的时候数据类型是确定的,编译器和执行器可以进行类型检测)。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

    10.2 泛型类

      声明包含泛型的类的格式如下:

         [访问修饰符]  class 类名<泛型1, 泛型2, >{

        [访问权限] 泛型类型标识  变量名称;

        [访问权限] 构造方法([<泛型类型>] 参数名称){}

        [访问权限] 泛型类型标识  getXxx(){}

        [访问权限] 返回值类型声明 setXxx(泛型类型标识 变量名称){}

      }

    比如:

    /*
     * 此处声明了一个包含泛型T的泛型类,T代表所有可能的类型
     * 而T的实际类型在MyGenerics类实例化时指定。
     */
    class MyGenerics<T>
    {
        private T x; // x为泛型成员
        public MyGenerics(T x)
        { // 构造方法的参数类型为泛型T
            this.x = x;
        }
    
        public void setX(T x)
        { // setX方法的参数类型为泛型T
            this.x = x;
        }
    
        public T getX()
        { // setX方法的返回类型为泛型T
            return this.x;
        }
    }

      创建泛型类的实例时,可以使用一对尖括号指定泛型的真正类型,但是需要注意的是,尖括号中的类型必须是类类型,比如对于10,不能为int,而是Integer

      泛型对象实例化格式如下:

        类名称<具体类> 对象名称 = new 类名称<具体类>();

    比如:

    //将T替换为Boolean,并调用一个参数 构造函数,传递的值为boolean类型
    MyGenerics<Boolean> gen1 = new MyGenerics<Boolean>(true);
    gen1.setX(false); // 调用setX方法,传递boolean值
    System.out.println(gen1.getX()); // 调用getX方法,返回boolean值
    
    
    //将T替换为Integer,并调用一个参数 构造函数,传递的值为int类型
    MyGenerics<Integer> gen2 = new MyGenerics<Integer>(10);
    gen2.setX(20); // 调用setX方法,传递int值
    System.out.println(gen2.getX()); // 调用getX方法,返回int值

    泛型使用的注意点:

      1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。

      2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。如:gen2 = gen1,则会报错。

      3、泛型的类型参数可以有多个。

      4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。

      5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");

      下面我们利用泛型来解决我们之前坐标的问题:

    //GenericsPoint.java

    public class GenericsPoint<T>
    {
        private T x;
        private T y;
    
        public GenericsPoint()
        {
        }
    
        public GenericsPoint(T x, T y)
        {
            this.x = x;
            this.y = y;
        }
    
        public T getX()
        {
            return x;
        }
    
        public void setX(T x)
        {
            this.x = x;
        }
    
        public T getY()
        {
            return y;
        }
    
        public void setY(T y)
        {
            this.y = y;
        }
    }

    使用过程如下:

    public class TestGenerics
    {
        public static void main(String[] args)
        {
            GenericsPoint<Integer> gen1 = new GenericsPoint<Integer>(10, 20);
            System.out.println("(" + gen1.getX() + "," + gen1.getY() + ")");
    
            GenericsPoint<Double> gen2 = new GenericsPoint<Double>(10.5, 20.6);
            System.out.println("(" + gen2.getX() + "," + gen2.getY() + ")");
    
            GenericsPoint<String> gen3 = new GenericsPoint<String>("东经180度", "北纬120度");
            System.out.println("(" + gen3.getX() + "," + gen3.getY() + ")");
        }
    }

      需要注意的是,泛型与子类继承的限制:在泛型操作中,子类的泛型类型是无法使用父类的泛型类型接收的。

      由前面的例子,我们知道,Object是所有类的父类,因此,所有的类型的实例都可赋值给声明为Object类型的变量。如:

    Boolean f1 = new Boolean(true);
    
    Integer f2 = new Integer(1);
    
    Object f = f1;   //OK
    
    f=f2;   //OK

      但在实例化泛型类时,将泛型指定为Object类型却不存在着和其他类型之间的兼容性:

    GenericsPoint<Integer> gen1 = new GenericsPoint<Integer>(10, 20);
    GenericsPoint<Double> gen2 = new GenericsPoint<Double>(10.5, 20.6);
    GenericsPoint<Object> gen = gen1; // gen1和gen类型并不兼容,发生编译错误

      除了可以定义包含一个泛型的类的定义之外,还可以包含多个泛型的类定义。其定义格式如下:

    [修饰符] class类名<T1, T2, …> {
        [修饰符] T1 f1;
        [修饰符] T2 f2;
        //....
    }

    示例如下:

    class MyGenerics<T1, T2>
    {
        private T1 x;
        private T2 y;
    
        public MyGenerics()
        {
        }
    
        public MyGenerics(T1 x, T2 y)
        {
            this.x = x;
            this.y = y;
        }
    
        public void setXY(T1 x, T2 y)
        {
            this.x = x;
            this.y = y;
        }
    
        public T1 getX()
        {
            return this.x;
        }
    
        public T2 getY()
        {
            return this.y;
        }
    }

      此时MyGenerics泛型类的实例化格式:

        给出泛型T1, T2的实际类型:

          MyGenerics<Integer, String> gen1 = new MyGenerics<Integer, String>(1001, "张三");

        也可以不给出T1, T2的实际类型。此时T1,T2将被默认为是Object类型:

          MyGenerics gen2 = new MyGenerics();

      但通常不会这么干。

      在泛型类中的泛型成员不能直接实例化,其实例必须要通过方法的参数传递给泛型成员,看下面的代码:

    public class MyGenerics<T>
    {
        private T[] array; //此处不能用new T[]实例化array
    
        public void setArray(T[] array)
        {
            this.array = array;
        }
    
        public T[] getArray()
        {
            return array;
        }
    }

    可以通过方法的泛型参数,将数组的实例传递给类中的泛型数组。看下面代码:

    String[] color = {"red", "green", "blue"};
    
    MyGenerics<String> gen = new MyGenerics<String>();
    
    gen.setArray(color);  //向泛型成员array传递实际的字符串数组
    
    String[] strs = gen.getArray();//读取泛型成员array的值,将其赋给字符串数组strs

    10.3 泛型接口

      在JDK1.5之后,不仅仅可以声明泛型类,也可以声明泛型接口,声明泛型接口和声明泛型类的语法类似,也是在接口名称后面加上<T>

      泛型接口定义格式:

        [访问权限]  interface  接口名称<泛型标识>{}

    比如:

    interface I<T1, T2>{
      void fun1(T1 t);
      T2 fun2();
    }

      泛型接口实现的两种方式:

        定义子类:在子类的定义上也声明泛型类型。

        定义子类:如果实现接口的子类不想使用泛型声明,则在实现接口的时候直接指定好其具体的操作类型即可。如果不指定,则此时泛型会自动变为Object

      实现泛型接口时,类在定义时可以不声明泛型接口中的泛型,此时接口中的泛型也会自动变为Object类型。如下:

    public class IC implements I
    {
    
        public void fun1(Object t)
        {…} //对应接口方法fun1的实现
    
        public Object fun2()
        {…} //对应接口方法fun2的实现
    
    }

    如果需要在实现泛型接口时,保留接口中的泛型,类在定义时就必须要保留泛型接口中的泛型声明。如下:

    public class IC<T1, T2> implements I<T1, T2>{
    
    public void fun1(T1 t){…} //对接口方法getT1的实现
    
    public T2 fun2(){…} //对接口方法getT2的实现
    
    }

    此时可以声明泛型接口的变量,并利用泛型实现类进行实例化。如下:

    I<String, Integer> i = new IC<String, Integer>();

    在实现泛型接口时,也可直接指定接口中的泛型的实际类型。如下:

    //实现接口I时,直接指定泛型T1、T2的类型
    class IC implements I<String, Integer>
    {
    
      //由于指定接口I中T1类型为String,fun1参数t的类型必须为String
        public void fun1(String t)
        {…}
    
      //由于指定接口I中T2类型为Integer,fun2返回类型必须为Integer
        public Integer fun2()
        {…}
    
    }

    同理,当包含泛型的类有继承关系的时候,如需保留父类泛型,需要在声明时加入父类泛型。如下:

    public class SubGeneric<T1, T2, T3> extends Generic<T1, T2>
    {
        private T3 f;
    
        public void setF(T3 f)
        {
            this.f = f;
        }
    
        public T3 getF()
        {
            return f;
        }
    }

    如果不保留父类中的泛型声明,则继承下来的T1T2自动变为Object类型。建议父类中的泛型声明在子类中都要保留。

    如果在继承时,不想保留父类中的泛型,但也不想使用默认的Object类型,此时可以直接指定父类中的泛型。如下:

    public class SubGeneric<T3> extends Generic<String, Object>
    {
        private T3 f;
    
        public void setF(T3 f)
        {
            this.f = f;
        }
    
        public T3 getF()
        {
            return f;
        }
    }

    10.4 泛型方法

      之前的所有的泛型除了可以为类中的属性制定类型之外,也可以定义方法,泛型方法所在的类中是否是泛型类本身是没有任何关系的。泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型,使用如下的格式定义泛型方法。

      泛型方法的简单定义:

        [访问权限] <泛型标识>  泛型标识  方法名称([泛型标识  参数名称])

      调用泛型方法和调用普通方法没有任何不同,只需传递含有具体类型的实参即可。比如:

    class Demo
    {
        public <T> T fun(T t)
        {    // 可以接收任意类型的数据
            return t;           // 直接把参数返回
        }
    }
    
    public class GenericsDemo
    {
        public static void main(String args[])
        {
            Demo d = new Demo();       // 实例化 Demo 对象
            String str = d.fun("张三");     // 传递字符串
            int i = d.fun(30);             // 传递数字,自动装箱
            System.out.println(str);        // 输出内容
            System.out.println(i);         // 输出内容
        }
    }

    可以将泛型方法和泛型类结合起来使用,如下范例:

    class Info<T>
    {
        private T value;
    
        public Info(T value)
        {
            this.value = value;
        }
    
        public T getValue()
        {
            return this.value;
        }
    
        public void setValue(T value)
        {
            this.value = value;
        }
    
        public String toString()
        {
            return this.value.toString();
        }
    
    }
    
    public class TestGenerics
    {
        public static <K> void conn(Info<K> a, Info<K> b)
        {
            System.out.println(a + " " + b);
        }
    
        public static void main(String[] args)
        {
            Info<Integer> infoInt1 = new Info<Integer>(10);
            Info<Integer> infoInt2 = new Info<Integer>(20);
            conn(infoInt1, infoInt2);
    
            Info<String> infoStr1 = new Info<String>("张三");
            Info<String> infoStr2 = new Info<String>("李四");
            conn(infoStr1, infoStr2);
        }
    }

    10.5 用泛型定义数组

      在使用泛型方法的时候,也可以传递或返回一个泛型定义的数组。例如:

    public class TestGenerics
    {
        public static <T> T[] fun1(T... arr)
        {
            return arr;
        }
    
        public static <T> void fun2(T arr[])
        {
            for (T val : arr) {
                System.out.print(val + "  ");
            }
            System.out.println();
        }
    
        public static void main(String[] args)
        {
            Integer i[] = fun1(1, 2, 3, 4, 5, 6);
            fun2(i);
    
            String str[] = fun1("aaa", "bbb", "ccc", "ddd");
            fun2(str);
        }
    }

      泛型定义的数组在接收数据的时候,必须保证数据的一致性。即传入的参数类型必须统一。假如修改传入的参数类型,使其与其他参数类型不一致,编译时就会报错。例如:

        Integer i[] = fun1(1, 2, 3, 4, 5.0f, 6); // 编译报错

    10.6 建立类型为泛型类的数组

      除了可以用泛型去定义数组外,还可以定义泛型类的数组。如果要建立泛型类的数组,需要注意new关键字后面不要加入泛型的实际类型名。如下:

     Generic<String>[]  gs;   //声明泛型类的数组
    
    //先对泛型数组进行初始化
    gs = new Generic[5]; //不要写成new Generic<String>[5]
    
    //再分别为每一个数组元素进行初始化
    gs[0]=new Generic<String>(); //为第一个数组元素赋值
    
    //

    实例如下:

    class Info<T>
    {
        private T value;
    
        public Info(T value)
        {
            this.value = value;
        }
    
        public T getValue()
        {
            return this.value;
        }
    
        public void setValue(T value)
        {
            this.value = value;
        }
    
        public String toString()
        {
            return this.value.toString();
        }
    }
    
    
    @SuppressWarnings("unchecked")
    public class TestGenerics
    {
        public static void main(String[] args)
        {
            Info<String>[] info = new Info[5];
    
            for (int i = 0; i < info.length; i++) {
                info[0] = new Info<String>("hello" + i);
                System.out.println(info[0]);
            }
        }
    }

    10.7 泛型类充当泛型的实例化类

      之前所讲解的全部泛型操作,都是直接用一个实际的类来实例化的。Java中也允许用一个泛型类来实例化另一个泛型类。这种结构称为泛型嵌套。

      首先定义了两个泛型类InfoDemo,如下所示:

    class Info<T, V>
    {    //  接收两个泛型类型
    
        private T var;
        private V value;
    
        public Info(T var, V value)
        {
            this.setVar(var);
            this.setValue(value);
        }
    
        public void setVar(T var)
        {
            this.var = var;
        }
    
        public void setValue(V value)
        {
            this.value = value;
        }
    
        public T getVar()
        {
            return this.var;
        }
    
        public V getValue()
        {
            return this.value;
        }
    }
    
    class Demo<S>
    {
        private S info;
    
        public Demo(S info)
        {
            this.setInfo(info);
        }
    
        public void setInfo(S info)
        {
            this.info = info;
        }
    
        public S getInfo()
        {
            return this.info;
        }
    }

    现在希望Demo类中info属性是Info类的这种类型。但是Info类本身使用的时候需要设置两个泛型。如下所示:

    public class GenericsDemo
    {
        public static void main(String args[])
        {
            Info<String, Integer> i = new Info<String, Integer>("张三", 30);
            Demo<Info<String, Integer>> d = new Demo<Info<String, Integer>>(i);
    
            System.out.println("内容一:" + d.getInfo().getVar());
            System.out.println("内容二:" + d.getInfo().getValue());
        }
    }

    10.8 通配符

      在开发中对象的引用传递是最常见的,但是在泛型类的操作中,进行引用传递的时候泛型类型必须匹配才可以传递,否则是无法传递的。例如:

    class Info<T>
    {
        private T var;
    
        public void setVar(T var)
        {
            this.var = var;
        }
    
        public T getVar()
        {
            return this.var;
        }
    
        public String toString()
        {
            return this.var.toString();
        }
    }
    
    public class GenericsDemo
    {
        public static void main(String args[])
        {
            Info<String> i = new Info<String>();
            i.setVar("xiaoxiao");
            fun(i);
        }
    
        public static void fun(Info<Object> temp)
        { //接收Object泛型类型的Info对象
            System.out.println("内容:" + temp);
        }
    }

    由前面的知识我们知道,这里企图利用Info<Object>来接收Info<String>类型的变量,编译出错。

      解决方法:

        1. 泛型对象进行引用传递的时候,类型必须一致。将fun函数改成如下形式:

    public static void fun(Info<String> temp){
      System.out.println("内容:" + temp);
    }

        2. fun()方法中Info参数的泛型取消掉(不建议):

    public static void fun(Info temp){
      System.out.println("内容:" + temp);
    }

        3. 利用通配符:

    public static void fun(Info<?> temp){
      System.out.println("内容:" + temp);
    }

      通配符?”,表示可以接收任意的内容。但是使用“?”声明的对象,此时不能修改。如下:

    public static void fun(Info<?> temp){
      temp.setVar("star"); //不能修改,报错
      System.out.println("内容:" + temp);
    }

      所以使用?”表示只能接收,不能修改

    10.8.1 受限泛型

      之前设置泛型类型的时候,只要是类就可以任意设置。但是在Java的泛型中可以指定一个泛型的上限和下限。

      在引用传递中,泛型操作中也可以设置一个泛型对象的范围上限和范围下限。范围上限使用extends关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。而范围下限使用super关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object 类。

      设置上限:

        声明对象:类名称<?  extends  类>  对象名称

        定义类:[访问权限]  类名称<泛型标识  extends 类>{}

      设置下限:

        声明对象:类名称<?  super  类>  对象名称

      

    ·设置上限实例(Info泛型类跟前面一样):

    public class GenericsDemo
    {
        public static void main(String args[])
        {
            Info<Integer> i1 = new Info<Integer>();
            Info<Float> i2 = new Info<Float>();
    
            i1.setVar(30);
            i2.setVar(30.1f);
    
            fun(i1);
            fun(i2);
        }
    
        public static void fun(Info<? extends Number> temp)
        { // 只能接收Number及其子类
            System.out.println(temp);
        }
    }

      如果你在main方法中增加下面几行,则编译报错,因为fun方法已经限制了上限,必须是Number类及其子类。如下:

    Info<String> i3 = new Info<String>();
    i3.setVar("xiaoxiao");
    fun(i3); //报错

      你还可以在声明Info的时候才用class Info<T extends Number>{}的形式,则此时利用Info<String> i3 = new Info<String>()就会报错。

        定义泛型类时,如果写成下面的代码:

          public class Info<T>{}

        相当于下面的定义方式:

          public class Info<T extends Object>{}

    ·设置下限实例:

      当使用的泛型只能在本类及其父类类型上应用的时候,就必须使用泛型的范围下限来配置。如下:

    public class GenericsDemo
    {
        public static void main(String args[])
        {
            Info<String> i1 = new Info<String>();     // 声明String的泛型对象
            Info<Object> i2 = new Info<Object>();     // 声明Object的泛型对象
    
            i1.setVar("hello");
            i2.setVar(new Object());
    
            fun(i1);
            fun(i2);
        }
    
        public static void fun(Info<? super String> temp)
        {//只能接收String或Object类型泛型
            System.out.print(temp + "、");
        }
    }

      在main方法中增加下面几行,则编译出错。因为fun已经设置了下限,则此时传递的必须是String类及其父类,如下:

    Info<Integer> i3 = new Info<Integer>();
    i3.setVar(30);
    fun(i3);

    感谢阅读。如果感觉此章对您有帮助,却又不想白瞟

                                     

  • 相关阅读:
    WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher
    .Net 2.0对文件传输协议(FTP)操作(上传,下载,新建,删除,FTP间传送文件等)
    我的WCF之旅(13):创建基于MSMQ的Responsive Service
    .net程序集强名称签名实践
    WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成
    .Net 2.0对文件传输协议(FTP)操作(上传,下载,新建,删除,FTP间传送文件等) 2
    WCF后续之旅(6): 通过WCF Extension实现Context信息的传递
    SilverlightCatchWcfError
    WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成
    WCF后续之旅(4):WCF Extension Point 概览
  • 原文地址:https://www.cnblogs.com/springl/p/13549947.html
Copyright © 2011-2022 走看看