Java:高级之泛型概念引入,泛型可以设置多个类型参数,泛型继承和泛型接口实现,限制泛型可用类型,泛型通配的方式,泛型方法,泛型方法限制泛型可用类型

2023-05-16

目录页

        泛型简介

        泛型类

        限制泛型可用类型

        类型通配声明

        泛型方法

问题引入

        如果我们需要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员变量的类型不同。那我们如何去做?

我们新建一个工程

做一个构造方法        public Cls1(int a){
                                        this.a =a;
                                            }

然后我们实例化这个方法的时候,给他传一个10        Cls1 cls1 = new Cls1(10);

然后我们输出cls1 里面的getData()                System.out.println(cls1.getData());

 运行结果

 同样的我们在做一个类,唯一的区别,所有的东西都一样,但是它a变成了        String a;

 运行结果

你会发现有两个类,这两个类的逻辑完全一样,所谓的逻辑就是里面的一些方法,包括构造方法,只是成员变量的类型不同

 那比如我们现在要做了double 类型的,是不是还得专门为double 创建一个类呀?

          

问题解决

        创建多个类文件,给每个类中的成员变量设置指定的数据类型

                缺点:这种方法会导致类的膨胀,重用性太差。

        创建一个类文件,给这个类中的成员变量设置Object数据类型。

                缺点:编译的时候正常,但运行的时候可能会异常

 创建多个类文件,给每个类中的成员变量设置指定的数据类型:

这样整个业务场景下来,整形一个,小数一个,字符一个,甚至里面会集成一些其他的类。

创建一个类文件,给这个类中的成员变量设置Object数据类型。:

我们会发现整形int ,打开它的类型继承

 你会发现Object是所有类型的父类

这种情况下我们完全可以把这两个删掉一个,

然后我们把Cls2改成Cls1        第21行

发现没写错,可是还报错        错误提示:     The constructor Cls1(int) is undefined

 发现是版本的问题

我们右击,然后选择properties

 然后看到JDK那行了嘛?

 

把对勾去掉

然后选择1.5版本往后的点击ok

 这个时候就没有报错了

 运行一下

 但是他有一个不太好的地方

这个Object导致整个类对这个变量的属性很模糊

可能会产生某些错误,

假如我现在输出“冰糖李子”,编码过程中误操作了,把“冰糖李子”这个字符串强制转换成整型数,你会发现编译的时候是ok 的

但是运行的时候会出现异常,类型转化异常

难免我们在操作过程中,随着代码量的增大,对里面的object的类型模糊不清晰,导致你程序员在编写代码的时候,做了一个误判,做了一个错误的转换,导致程序崩溃

面向于 这种情况发生,我们有一个非常好的解决办法,就是泛型

一、泛型简介        JDK1.5之后引入 

        泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的

        泛型的原理就是“类型的参数化”,即把类型看做参数。也就是说把所要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值的占位符一样。

        简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。

        泛型可以提高代码的扩展性和重用性

那么泛型怎么做?

示例----泛型类

        public  class GenClass<T>{

                private   T  obj;

        

                public  GenClass(T   obj){

                        this.obj = obj;

                }         

                public  T  getobj(){

                        return obj;

                }

                public  void   setobj(T  obj){

                        this .obj  = obj;

                }

}

泛型的意思就是就是在class后面添加一个<T>,用T去替代里面未名的数据类型

比如说我们的代码可以在Cls1后面加上<T>,用一个T来表示

然后new 的时候要把这个东西用上,你在实例化的时候呈现,他是一个整形

Cls1<Integer> cls1= new Cls1<Integer>(10);

同样使用cls2的时候,落地的时候,我让他都是字符串

Cls1<String> cls2 = new Cls1<String>("冰糖李子");

 运行结果

我们用<T>替代了刚才写的Object,在使用的时候,在程序员编码的过程当中,实例化类的时候,在<>里面,去告诉操作系统,包括程序员看代码的时候更具象的把他表达出来

这就叫做泛型

泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的

当你转换出现问题的时候,你硬要把string转化成Integer,他就会报错,

刚才我们用object的时候它没有任何的报错,

只是在程序运行的时候出现类型转换异常

(这就是泛型引入的除了程序员可读之外,还有个好处就是防止你的误操作,误转换)

Cannot cast from String to Integer

         泛型的原理就是“类型的参数化”,即把类型看做参数。也就是说把所要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值的占位符一样。:

我们习惯了函数调用,习惯了传参public Cls1(T a),

那实际上类型也可以以参数的形式传过来,为了区别我们用来了<>

我们在这个类初始化,实例化的时候    Cls1<Integer> cls1= new Cls1<Integer>(10);

我们直接把泛型类里面的,涉及到泛型的位置,用Integer替代进去

也就是说Cls1类里面的  a变量的的类型,        T a;

是根据我们真正实例化的时候,用<>的方式传参过去的,来确定里面a的类型

,同样的一个类,你在使用实例化的时候,给泛型传的类型不一样,这个类最后体现的结果也是不一样的

                                Cls1<Integer> cls1= new Cls1<Integer>(10);
                                    System.out.println(cls1.getData());
                                    Cls1<String> cls2 = new Cls1<String>("冰糖李子");
                                            System.out.println(cls2.getData());

 二、泛型类及特点

        1、泛型的类型参数可以是泛型类

        2、泛型类可以同时设置多个类型参数

        3、泛型类可以继承泛型类

        4、泛型类可以实现泛型接口

 泛型的类型参数可以是泛型类:

也就是说你这个<T>除了普通变量Integer   和String 外,他也能接受是个泛型

泛型类可以继承泛型类:

跟我们普通类的继承是一样的,当然你泛型可以在继承过程中实现多个类型参数

你的父类型是个泛型,他里面只有一个<T>,你完全可以除了<T>,多一个<K> 呀,<T2>等等。

一般我们泛型用的<T>,不是写死的 ,就像形参的名字,你愿意起什么都可以的,

按照程序员编码习惯,一般是<T>呀,<K>,<B>呀,经常用的几种写法

泛型类可以实现泛型接口:

这个继承和实现,和我们普通类的继承和实现没有很大的区别,唯一的区别就是体现在参数上面,和泛型上面

泛型类可以同时设置多个类型参数:

现在再做一个类Cls2,多个参数用,隔开        class  Cls2<T,T2>

构造方法                        public Cls2(T a,T2 b){
                                                this.a =a;

                                                this.b =b;
                                            }

有两个变量,                T a;
                                    T2 b;

这两个变量的具体类型为止

同样的b的类型是T2,        public T getData(){
                                                return a;
                                            }
    
                                            public T2 getData2(){
                                                        return b;
                                                    }

我们在真正做他的时候,<>里面的顺序一样,构造方法要传参

   

 运行结果

           

 这面要注意一个问题,比如说我现在有一个Cls4,两个泛型我们都是整型数

这面显示一个错误 

he constructor Cls2<Integer,Integer>(int, String) is undefined

你这面都写integer,但是他会认定你传进来的构造方法是一个字符串,所以他会失败

 我改成10就OK啦

 

 那么我们可以把上面两个整型数拿来相加嘛?

运行结果 

 暂时看到编译是ok 的,加也是可以正常加的

 那如果一个是Integer 一个是String呢

数据类型不同的时候相加,看结果

 

运行结果        

也能加        

用System.out.println可加的                

但是会给人家错误的认为,感觉不同的数据类型都可以拿来加,                                        

但是你要知道System.out.println这面的加号是起到一个连接的作用        

 如果我们单独给他拎出来,他会出现一个什么样的情况呢?

        int sum = 0;
            sum = cls4.getData()+cls4.getData2();
            System.out.println(sum);
            

 

 运行结果

这样子写也行 

泛型的类型参数可以是泛型类:

Cls1<Integer> cls1= new Cls1<Integer>(10);里面也可以放入泛型

比如说里面就是Cls1,Cls1他就是个泛型,这边我用Integer给他确认下来

                        Cls1<Cls1<Integer>> cls1

另一面也一样

这面出现问题是因为不能写10,

 因为他应该是Cls1<Integer>的实例化                               

泛型类里面的类型还是个泛型,你new的时候和前面一样,这面的构造方法呢?

 构造方法传过来的时候 ,T不就变成Cls1<Integer>

我们在输出的时候,System.out.println(cls1.getData());

这个Data,get出来是一个泛型

因为Cls1他是最外面的那一层,你获得出来的应该是Cls1<Integer>,

Cls1<Cls1<Integer>> cls1= new Cls1<Cls1<Integer>>(new Cls1<Integer>(10));
     

这个Cls1<Integer>又是一个类,你如果访问到a,你还得getData一次

        System.out.println(cls1.getData().getData());

这个时候我们才能把10抓出来

 运行结果

泛型类可以继承泛型类:

我现在的Cls2和Cls1是重复的呀,这样写也是一种浪费 

可以         class Cls2<T,T2> extends Cls1<T>,继承过来儿子比父亲多点特性,是ok的,

就是构造方法这面我们需要做一些修改

错误提示:        Implicit super constructor Cls1<T>() is undefined. Must explicitly invoke another constructor

因为他要调用父亲的            super(a);        你在子的构造方法中,调用父亲的构造方法来构造自己

剩下的一项,   this.b=b;     作为数据的初始化

 可以把父类有的去掉

 然后我们使用一下

Cls2<Integer,String> cls = new Cls2<Integer,String>(100,"冰糖李子");

用法还是一样的,就是我们在构造Cls2的时候,不要写那么多代码,一些东西是从Cls1继承过来的 

                                                                                                                                                                     

那如果他是继承的话,有一些函数,比如说           void printInfo();        假设他现在是个泛型,

同时又是一个抽象的方法                            abstract void printInfo(); 

这边是一个抽象带有泛型的类        abstract class Cls1<T>        也是ok的

也是遵循以前继承的道理,你要把里面 的printInfo()实现出来

错误提示:         Multiple markers at this line
                            - Cannot instantiate the type 
                             Cls1<Cls1<Integer>>

因为abstract class Cls1<T>        是一个抽象

抽象类是不能初始化的,可以去掉    第39行,第40行

     代码如下

 

 运行结果

 泛型类可以实现泛型接口:

我们比如说再来一个interface cls3<T>        也是泛型

里面有一个抽象方法

这是一个接口

跟以前的写法也是一样的,

以前的写法  是      没有<T>,然后

                                        interface cls3
                                                {

                                                    abstract void printInfoCls3(String t);
                                                }

这就是我们以前说的接口,无非在这个接口上加了一个泛型

也就是这个抽象方法,可以实现,输出整型数,字符串都是ok的

 那我们的cls2可以继承cls1

 Cls2也可以implements这个接口

 class Cls2<T,T2> extends Cls1<T> implements Cls3<T>

既然你要实现接口的话,就要实现接口里面的方法

 我们在使用Cls2的时候        第53行

                        cls.printInfoCls3(100);

虽然Cls2那面写的都是T,但是      public Cls2(T a,T2 b){
                                                        super(a);
                                                                this.b=b;
                                                    }

这两个T不一样                public void printInfoCls3(T t) {
                                                // TODO Auto-generated method stub
                                                System.out.println(t);

                                                        }

下面这个T是隶属于        interface Cls3<T>这个接口的,取决于你传递进来是什么数据

上面的那个T是        Cls2<Integer,String> cls = new Cls2<Integer,String>(100,"冰糖李子");里面的,它取决于你给他实例化的时候,设定的是具体的哪个参数

当然,刚好我们写了Integer   和    cls.printInfoCls3(100);  里面的100

如果我们把        cls.printInfoCls3(100); 改成字符串呢?        第53行

错误提示:The method printInfoCls3(Integer) in the type Cls2<Integer,String> is not applicable for the arguments (String)        不行了

所以说T如果产生某种冲突的话,也是不允许的

 你这时候即继承了Cls1都叫T,又实现了这个接口,在这种情况下,你如果名字一样

你要遵循在class Cls2<T,T2> extends Cls1<T> implements Cls3<T>

        下面所有的T表示的都是同一种类型

不然他这面会产生类型上的冲突

 

 这种情况下改变一个T3试试

 我把class Cls2<T,T2,T3> extends Cls1<T> implements Cls3<T3>

里面的T3去掉可以吗?        第22行

也不行,因为public void printInfoCls3(T3 t)

必须在class Cls2<T,T2,T3> extends Cls1<T> implements Cls3<T3>

被包含

 你要支持T3,你就要做出一系列的修改

Cls2<Integer,String> cls = new Cls2<Integer,String>(100,"冰糖李子");你要跟T3绑定起来

而且必须是一个String

如果这时候你把 cls.printInfoCls3("测试");   改成cls.printInfoCls3(100);

也不行

 完整代码

 

运行结果

 注意:泛型类可以实现泛型接口,

但是要注意,既要继承又要实现接口的 时候,要保证它里面具备了这些泛型的占位符

三、限制泛型可用类型

        在定义泛型类别时,默认在实例化泛型类的时候可以使用任何类型,但是如果想要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口

        当没有指定泛型继承的类型或接口时,默认使用extends Object,所以默认情况下任何类型都可以做为参数传入

                         

         

 当没有指定泛型继承的类型或接口时,默认使用extends Object,所以默认情况下任何类型都可以做为参数传入:

这就是我们之前的做法

 

 系统在编译它的时候是默认加了一个extends Object

你写不写它的效果是一样的,也就是对T他没有任何的限制,所以在使用它的时候任何类型都可以作为参数传入

 

 那如果某种场景我们必须对这个T做一些监管,限制使用泛型类型,

可以使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口

如果我变成了        abstract class Cls1<T extends String>

如果你这样写,你没办法给这面赋一个整型数的

 

 这个时候只能把

    Cls2<Integer,String,String> cls = new Cls2<Integer,String,String>(100,"冰糖李子");

Integer    变成   String类型        这面的100也只能改成字符串

Cls2<String,String,String> cls = new Cls2<String,String,String>("","冰糖李子");

这就是我们说的限定了T,

 

 可以用继承,可以用某个接口

我们添加一些具体的写法进来

我要求这个T必须继承Animal                                               

一写进来Cls2<String,String,String> cls = new Cls2<String,String,String>("","冰糖李子");

必须变成                                                                                                               

Cls2<Dog,String,String> cls = new Cls2<Dog,String,String>(new Dog(),"冰糖李子");

 

 那如果我现在有一个接口,接口里面有一个抽象的方法                                                                             
interface Move                                                                                                                      
{                                                                                                                                                
    abstract void test();
}

如果你对于T做一些限制要求,必须要实现Move这个接口                                                 

我们以前实现接口是        class A implements Move {              

                                                }       这样一个写法

A这面去实现Move里面未实现的方法·        叫做test

 如果这面也用Move的话                                                                                                

abstract class Cls1<T extends Move>                                                                    

是用  extends      abstract class Cls1<T extends Move>                                                           

还是    implements     abstract class Cls1<T implements Animal>                                      

发现还是得用extends      abstract class Cls1<T extends Move>                                         

                                                                                                                                        

注意他跟我们传统的        implements是不一样的                                      

 

 

 为什么        abstract class Cls1<T extends Move>           多了extends?

因为他可能继承某个类        ,或者限定了它实现某个接口                                             

四、类型通配声明

同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,不能互相赋值。

        Generic<Boolean> f1 = new Generic<Boolean>();

        Generic<Integer> f2 = new Generic<Integer>();

        f1 = f2;          //发生编译错误

        Generic<Object> f= f1 ;         //f1和f类型并不兼容,发生编译错误f=f2;

                                                       //f2和f类型同样不兼容,也会发生编译错误

        泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型类通配符(?)声明泛型类的变量就可以解决这个问题。

新建一个工程

 现在有一个Cls1,实例化一下(记得抽象类不能实例化)

 同样的我们再来一个c2,来一个Double类型的,                                                                               

然后我们让c1 = c2;        他是不允许的                                                                    

错误提示        Type mismatch: cannot convert from Cls1<Double> to Cls1<Integer>

不能把c2转化为c1,因为Double和Integer是有区别的                                             

 那如果我们现在有Object,行不行?                                                                                       

更大的类,去做一些匹配                                                                                                 

错误提示   Type mismatch: cannot convert from Cls1<Object> to Cls1<Integer>

他也是不行的                                                                                                                             

反过来也不行

类型通配声明        例子

        泛型通配的方式

      ·  “?”代表任意一个类型                                                                   

                Generic<Boolean>  f1 = new Generic<Boolean>();              

                Generic<?> f = f1;                                                                                   

        ·和限制泛型的上限相似,同样可以使用extends关键字限定通配符的上限              

                Generic<Dog> f1 = new Generic<Dog>();                        

                Generic<? extends Annimal> f =f1;                                                 

       · 还可以使用super关键字将通配符类型限定为某个类型的下限

                Generic <Animal> f1 = new Generic<Animal>();                                           

                Generic<? super Dog> f =f1;                                                                                  

                        //它必须是Dog 的父亲

                                                                                                                                                                                                                   

也就是说我们用一个<?? 就可以搞定了,我们声明这个c3;

这个时候你会发现c3可以引用c1

 同样的c3可以引用c2;

这个就是我们说的通配符,用?来表示

和限制泛型的上限相似,同样可以使用extends关键字限定通配符的上限:

也就是说,你可以管理这个问号

比如说我们来个c4,我们c4这面要求是一个Dog

比如说我的c4里面的泛型是一个Dog

这种情况下我的c3可以等于c4,因为这个问号是通配所有的

 那如果对于问号管制的话,        Cls1<? extends String> c3;

发现很多东西都不能用了,因为他必须是String 的子类或者String

 那如果我们把他改成Integer 呢?

发现只有c1这一条是可以的

 如果改成Animal                 Cls1<? extends Animal> c3;呢?

1和2 不行,4是可以的,                对?号产生了一定的范围

只有它的上限是Animal 的时候才可以

 还可以使用super关键字将通配符类型限定为某个类型的下限:

比如我这面用super,就不行了,它必须是Animal的父亲

 如果把这面改成一个Dog 呢?

 改成Animal也是可以的

     

 五、泛型方法

        不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:

        访问修饰符  <泛型列表>  返回类型  方法名  (参数列表){

                实现代码

        }

        在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明

        类中其他方法不能使用当前方法声明的泛型

        提示:是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前

 不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。:

也就是说,这个泛型是属于方法的,不是属于类的,也就是说我们可以在普通类中去定义一个泛型方法。

类中其他方法不能使用当前方法声明的泛型:

也就是说我这个泛型是有时效性的。它是有区域性的,有一个它的作用域,有点像局部变量。你在当前方法中有泛型列表,在其他方法中不能用

是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前:

也就是我们说的普通类可以有泛型方法

                                              

你有了泛型类,为何还要泛型方法呢?

新建一个工程

假设classA里面有一个泛型<T>

          

错误提示:Multiple markers at this line
    - Syntax error, type parameters are only available if source level is 1.5 or 
     greater

需要换到1.5版本以后的

找到这里 

 

 看见JDK那行了嘛

对勾去掉,换成1.5版本以后的

 然后就没有错误提示了

 运行结果,可行

 那如果我在这里打印一个100呢?

编译都通不过

错误提示:        The method printInfo(String) in the type A<String> is not applicable for the arguments (int)

因为他要求你传String,但是你的arguments是100

 等于说这个方法,

被里面的泛型给限制了                class A<T>
                                                 {
                                                        public void printInfo(T t){
                                                                  System.out.println(t);
                                                               }
                                                        }

那么泛型方法是什么意思呢?我不应该让我的t         被你的类限制掉

比如说我现在有一个class  B

我可以把他声明为普通的方法        class B<T>

注意:泛型方法是        

  访问修饰符  <泛型列表>  返回类型  方法名  (参数列表){

                实现代码

        }

也就是说我的泛型列表可以放在返回类型的前面        void前面我放一个<T>

                public <T> void printInfo(T t){
                                System.out.println(t);
                            }

 

 运行结果

我们得出的结论,泛型方法更加灵活,它可以不受泛型类的约束

如果你泛型类在实例化的时候,确定了泛型的类型,以及你方法里面所有的东西   T  都被定死掉了

就出现了刚才我们的A         只能打哈哈,不能打1234

这个东西比我们以前做的方法的重载,更加的牛逼

 以前方法重载是什么意思呢?

                public void print1(int a){
                        System.out.println(a);
                    }

再来个char a 这就叫方法的重载

                public void print1(char a){
                                        System.out.println(a);
                    }

        

你即便方法重载也要写这么多遍

那泛型方法它的好处,比方法重载更加牛逼

不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法:

也就是我的T         第11行,只对我本方法有效

跟这个类class  B没有关系,跟其他的方法也没有关系

也就是说你能不能用到t        呀?

错误提示:  t cannot be resolved to a variable

 当然泛型列表中的个数也可以做多个                

        public <T,T2> void printInfo(T t,T2 t2){
                        System.out.println(t+t2);
            }
    

这其实也是个方法的重载,但是这面t2不让加   

错误提示:he operator + is undefined for the argument type(s) T, T2

 分开写呢?

 

 为什么不让加呢?因为他如果在后面实例化,确定类型以后是可让加的,

,你这面没有实例化,它的方法类型是不确定的,不让你乱加

 

 当然        public <T,T2> void printInfo(T t,T2 t2){
                                System.out.println(t);
                                System.out.println(t2);
                    }

也是泛型方法,只是对                public <T> void printInfo(T t){
                                                        System.out.println(t);
                                                            }

做了重载

我们重载的意思是,返回值,跟这个名字一样,参数列表可以不一样

 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明:

也就是说            public <T,T2> void printInfo(T t,T2 t2){
                                System.out.println(t);
                                        System.out.println(t2);
                                    }
可以返回一个T    public <T,T2> T printInfo(T t,T2 t2){
                                           System.out.println(t);
                                            System.out.println(t2);
        
                                                return t;
                                         }

这样子是可以的

 

类型声明:        public <T,T2> T printInfo(T t,T2 t2)
局部变量:                System.out.println(t);

五、泛型方法

        什么时候使用泛型方法,而不是泛型类呢?  

                添加类型约束只作用于一个方法的多个参数之间,而不涉及类中的其他方法时。

                施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数

                     

  添加类型约束只作用于一个方法的多个参数之间,而不涉及类中的其他方法时。:

我们不希望这个方法当中的数据类型被这个类给限制了,而是自由的我想打印什么,都可以,不被类的方法限制的时候

施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数:

类型约束:

也就是说        public <T> void printInfo(T t)这个泛型T也可以进行类型的约束

    

   前面加上

                        class Animal
                                {    
                                    public void eat(){
                                        System.out.println("动物吃");
                                       }
                                }

                                          

                             class Dog extends Animal
                                {
                                    public void eat(){
                                        System.out.println("啃骨头");
                                            }
                                        }

                                class Cat extends Animal
                                {
                                    public void eat(){
                                        System.out.println("吃鱼");
                                            }
                                        }

中间加上    

他必须是一个上限,是Animal的子类        

                            public <T extends Animal> void printInfo2(T t){
                                                t.eat();
                                            }

这就叫类型约束

这时候来个            b.printInfo2("哈哈");  可以嘛?

错误提示:Bound mismatch: The generic method printInfo2(T) of type B is not applicable for the 
 arguments (String). The inferred type String is not a valid substitute for the bounded 
 parameter <T extends Animal>

 

 这时候只能                b.printInfo2(new Dog());
                                    b.printInfo2(new Cat());
                                    b.printInfo2(new Animal());

也就是我们之前讲的对泛型<T>的限制,放在泛型方法里面是同样适用的

 

 运行结果

 静态方法:

我们比如把

public <T extends Animal> void printInfo2(T t)变成

public static <T extends Animal> void printInfo2(T t)

警告:        第71行,第72行,第73行

The static method printInfo2(Dog) from the type B should be accessed in a static way

 

 出现这种情况,完全可以    B.printInfo2(new Dog());

静态方法就是要求我们这样做

不用非得实例化,类的名字加上方法名,做一个静态的调用

静态方法不能使用其所在类的类型参数:

因为静态方法本身就是和这个类脱离的

注意:这个T会被重置,编译的时候

第33行的T      public <T> void printInfo(T t)         到最后会变成object

第44行的T     public static <T extends Animal> void printInfo2(T t)                  到最后会变成Animal

 

 注意:不管你是泛型方法还是普通方法,你不能搞一摸一样的

Duplicate method print1(int) in type B

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java:高级之泛型概念引入,泛型可以设置多个类型参数,泛型继承和泛型接口实现,限制泛型可用类型,泛型通配的方式,泛型方法,泛型方法限制泛型可用类型 的相关文章

  • 配置 OPC UA 服务器 (Milo)?

    我刚刚查看了 Eclipse Milo 项目 https projects eclipse org proposals milo https projects eclipse org proposals milo 这对于 开放 OPC UA
  • 使用缩略图器,我可以制作具有相同高度和宽度的缩略图,无论图像大小如何

    In 缩略图器 http code google com p thumbnailator 我正在制作缩略图 如果图像大小是 400 300 并且如果我执行以下操作 Thumbnails of new File original jpg si
  • 无法从 java 发送 48681 字节消息以保护 wcf 服务

    我必须使用相互身份验证从 java 调用安全的 WCF 服务 一切工作正常 除了我无法发送大小超过 48680 字节的消息 因此 48680 字节的消息已成功发送 但 48681 字节的消息未成功发送 并且 java 应用程序因读取超时异常
  • 如何在异常处理程序中访问访问请求主体

    我们有一个 Spring Boot 应用程序 我们的控制器期望在我们的端点之一中有一个 XML 文档元素 PostMapping value api v1 do stuff consumes APPLICATION XML VALUE pr
  • 使用 WebDriver 暂时绕过隐式等待

    当使用隐式等待时 正如这里所建议的 https stackoverflow com a 10950905 56285 我仍然有时想要断言即时元素不可见或不存在 换句话说 我know有些元素应该隐藏 并希望我的测试做出这样的断言fast 而不
  • 使用 Bouncy Castle 重建 ED25519 按键 (Java)

    Bouncy Castle 的最新 测试版 版本 bcprov jdk15on 161b20 jar 支持 ED25519 和 ED448 EC 加密以进行签名 我设置了这个完整的工作示例 它按预期工作 我的问题 我是否正确重建了私钥和公钥
  • 如何在我的 HttpClient 执行器中遵循单一职责原则?

    我在用RestTemplate http docs spring io spring docs current javadoc api org springframework web client RestTemplate html as
  • Java OR 运算符优先级

    如何在 Java 中以 if 的方式链接条件语句b是假的 不如不检查c If a and c是假的 并且b是真的 确实c会被检查吗 if a b c 我正在寻找 PHP 所拥有的类似功能 但两者之间存在差异OR and 爪哇 如果左操作数是
  • Java Sound可以用来控制系统音量吗?

    Java 声音优惠FloatControl各种声音线路功能的实例 以及MASTER GAIN http docs oracle com javase 7 docs api javax sound sampled FloatControl T
  • 使android listview布局可滚动

    我有一个 xml 文件 其布局为 ASCII 形式 ImageView TextView List
  • 堆内存与对象内存

    根据一篇关于Java内存和特性的论文 内存分数分为两种类型 堆内存 即应用程序在运行时消耗的内存 对象内存 即程序中使用的各种对象分配的内存 例如整数和字符串等 他们的意思是stack当他们说时的记忆object记忆 或者它们是什么意思 很
  • 在类路径中使用通配符调用 java 失败

    我当前目录中有一些 jar 它们都需要位于类路径中 因此我想对类路径使用通配符约定 命令行是 java exe classpath org python util jython args 但是我收到这个错误 Exception in thr
  • 如何使用 Tomcat 启用浏览器缓存静态内容(图像、css、js)?

    如何使用 Tomcat 启用浏览器缓存静态内容 图像 css js 首选的解决方案是编辑 spring MVC 配置文件或 web xml 尝试 改变值
  • 字节流和字符流

    请解释一下什么是字节流和字符流 这些究竟意味着什么 Microsoft Word 文档是面向字节的还是面向字符的 Thanks 流是一种顺序访问文件的方式 字节流逐字节访问文件 字节流适用于任何类型的文件 但不太适合文本文件 例如 如果文件
  • 如何在不打开浏览器的情况下查看 Android 应用程序中的网页?

    嘿 我正在开发一个 Android 应用程序 我想连接到该应用程序内的网络 不过 我在某种程度上尝试过 WebView 但它在我的目录中显示的文件很好 但当连接到 google com 时 它显示错误 然后我添加了这个文件
  • 在JAVA中将数据写入.txt文件?

    我想知道是否是在JAVA中将计算的数据写入文本文件 我的 JAVA 代码是一个基于 GUI 的 gpa 计算器 我只想添加一个 JButton 和 ActionListener 它将类名 GPA 点和计算出的 GPA 写入 txt 文件 这
  • 在 Struts 2 中使用单个文件标签上传多个文件

    我想使用单个 Struts 2 文件标签上传多个文件 就像在 Gmail 中一样 我们使用 CTRL 键来选择多个文件来附加多个文件 我知道如何上传多个文件 但我想使用单个文件标签 我在一个小画廊应用程序中上传多个文件 如果您的操作已设置为
  • 当键位于父类中时,如何将一对多集合映射到连接的子类

    我想将一对多集合映射到子类 但集合的键是父类的属性 目前我正在映射 AbstractFoo Foo 和 Bar 类 如下所示
  • 如果垃圾收集器没有删除未引用的对象,它们还能运行吗?

    如果一个对象正在等待垃圾收集 但包含一个在该对象的最后一个引用更改时正在运行的线程 那么该线程是否仍会运行并且代码是否仍会执行 那么您是否可能有一堆应该删除的幽灵对象 但它们对您的代码产生了影响 你如何防止这种情况发生 有没有办法让对象知道
  • 如何在Java中添加两个“卡”的值?

    我正在开发一个项目来模拟二十一点游戏中的第一笔交易 到目前为止 程序创建了两张随机等级 ACE 到 KING 和随机花色的牌 我正在努力创建一个切换表或 if else 梯形图 将两张卡的附加值分配为可变分数 下面的代码从概念上代表了我想要

随机推荐