Java泛型之<T>

  这里不讲泛型的概念和基础知识,就单纯的就我的理解说一下泛型中的<T>

一. <T>

  下面的一段码就可以简单使用了<T>参数,注释就是对<T>的解释。

package xayd.hjj;

import java.util.ArrayList;
import java.util.List;

class A{}
class B{}
class C{}
class D extends C{}

public class Test<T> extends ArrayList<T>{

        public static <T> void main(String[] args) {  

        Test<C> testC = new Test<C>();
         // testC = new Test<B>();  //编译错误
        //testC = new Test<D>();   //编译错误
        //一旦指定了参数为C这个类就不能把这个对象在赋值为其他类型,即便是其父类

        testC.add(new C());
        //testC.add(new B());       //编译错误,不能在C类的容器中存放无关C的类B的对象
        testC.add(new D());        //编译正确,因为父类容器可以放置父类对象,同时也可以放置子类对象

        C testC1 = testC.get(0);
        C testC2 = testC.get(1);    //从父类容器中拿出子类对象,用最初传递的参数类型接收
       // D testC3 = testC.get(1);    //编译错误
        D testC4 = (D) testC.get(1); //从父类容器中拿出子类对象,用最初传递的参数类型的子类对象接收,要进行强制类型转换
        System.out.println(testC1); //[email protected]
        System.out.println(testC2); //[email protected]
        System.out.println(testC4); //[email protected]

    }

}

  其实继承ArrayList的目的是为了使用add和get方法,以便测试,其实你也可以自己实现类似于add和get的方法。

   ArrayList关于add和get方法的实现如下:

private transient Object[] elementData;
private int size;
public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}  

public E get(int index) {
    RangeCheck(index);
    return (E) elementData[index];
}

  下面还有一段和上面有点类似的代码。如下:

package xayd.hjj1;

import java.util.ArrayList;
class A{}
class B{}

class C{}

class D extends C{}

public class Test<T> extends ArrayList{  //注意这里不是ArrayList<T>,其实应该相当于ArrayList<Object>

    public static void main(String[] args) {
        Test<C> testC = new Test<C>();
        // testC = new Test<D>();//同样编译有错误
        testC.add(new C());
        testC.add(new A());   //奇怪的是可以通过编译
        testC.add(new B());   //这样也可以通过编译
        testC.add(new D());

        //C objC = testC.get(0);  //编译不能通过
        Object objC1 = testC.get(0); //编译通过
        C objC2 = (C) testC.get(0); //编译通过

        //C objC3 = (C) testC.get(1); //这样的结果编译可以通过,但是运行毫无疑问会抛出ClassCastException
        A objC4 = (A) testC.get(1);  //编译通过,不过这样程序员必须知道容器中第二个对象是A类型.
        Object objC5 = (A) testC.get(1); //编译通过
        Object objC6 = (Object) testC.get(1); //编译通过

    }

}

  这段代码和第一段代码的区别就是继承ArrayList时的不同。第一段代码是Test<T> extends ArrayList<T>,这段代码是Test<T> extends ArrayList。从代码运行的结果可以看到这段代码是不安全的,这段代码用testC调用add方法时参数传递没有限制,并不像第一段代码会出现编译错误。而且用get方法拿出时必须进行类型转化,即使你执行了这样的代码 C objC3 = (C) testC.get(1),编译期并不能检查到错误,而是在运行时抛出ClassCastException。

  在这里我对于ArrayList<T>和ArrayList这两种不同写法的解释如下(可能也不太准确,如有错误还请指正):

  Test<T> extends ArrayList<T>: Test<C> testC = new Test<C>(),这样Test和ArrayList的参数类型都为C,因此add方法参数只能传递C和C的子类。从add方法的源码中可以看到是这样添加元素的:elementData[size++] = e,在这里elementData其实是一个Object类型的数组,但是因为你限制了T的参数为类C的类型,所以编译器会限制这Object必须为C类型(或者是它的子类),所以在你加入了其他与类C无关的对象的时候编译是不能通过的。

  Test<T> extends ArrayList: Test<C> testC = new Test<C>(), 这样Test参数类型为C,而ArrayList的参数没有传递,因此默认为Object(public class ArrayList<E>,ArrayList类在源码中是这样的实现形式)。所以只要是Object类型都可以添加,所以才会导致编译期编译器可以无限制的添加任类型却没有任何错误,而运行期拿出时一旦转型错误便抛出异常。

  二.关于上面的代码导致的两种不同结果,是因为ArrayList本身的实现导致的。所以自己实现了一个简单的统一的类重新解释T.

package xayd.hjj2;

import java.util.ArrayList;

class A{}
class B{}

class C{}

class D extends C{}

public class Test<T>{ 

        private T obj;

        public T getObj() {
            return obj;
        }

        public void setObj(T obj) {
            this.obj = obj;
        }

        public static <T> void main(String[] args) {
            Test<C> testC = new Test<C>();
            testC.setObj(new C());
            C objC = testC.getObj();
            System.out.println(objC);   //[email protected]

            // testC.setObj(new B());  //编译错误

            testC.setObj(new D());    //编译成功
            C objC1 = testC.getObj();
            System.out.println(objC1);  //[email protected] 

        }
}

  这段代码就比较简单清晰了,Test类和类的成员变量参数类型一致,因此不会有不同的结果。

  

时间: 2024-11-03 22:51:19

Java泛型之<T>的相关文章

JAVA泛型之&lt;? extends T&gt;:(通配符上限)和&lt;? super T&gt;(通配符下限)

一.通配符上限和通配符下限接受的类型 通配符上限:<? extends T> 通配符下限:<? super T> 以下代码是测试结果,注释为解释说明 1 package xayd.hjj; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 class A{} 7 class B extends A implements F{} 8 class C extends A{} 9 class D extends A{

Java泛型中extends和super的理解(转)

E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类) K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定的java类型(无限制通配符类型) S.U.V – 2nd.3rd.4th types Object – 是所有类的根类,任何类的对象都可以设置给该Object引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型T.E等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换. ?

浅谈Java泛型之&lt;? extends T&gt;和&lt;? super T&gt;的区别

关于Java泛型,这里我不想总结它是什么,这个百度一下一大堆解释,各种java的书籍中也有明确的定义,只要稍微看一下就能很快清楚.从泛型的英文名字Generic type也能看出,Generic普通.一般.通用的,是一个概括性的词,那么泛型从名字上也就好理解了,它是一种通用类型,是java中各种类型的概括. ?是java泛型中的通配符,它代表java中的某一个类,那么<? extends T>就代表类型T的某个子类,<? super T>就代表类型T的某个父类. 这里我们先定义一组

Java泛型 通配符? extends与super

本文来源:https://i.cnblogs.com/EditPosts.aspx?opt=1 感谢博主.本文仅供参考学习. Java 泛型 关键字说明 ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object extends 示例 static class Food{} static class Frui

java 泛型详解(普通泛型、 通配符、 泛型接口,泛型数组,泛型方法,泛型嵌套)

JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型. 1.Java泛型  其实Java的泛型就是创建一个用类型作为参数的类.就象我们写类的方法一样,方法是这样的method(String str1,String str2 ),方法中参数str1.str2的值是可变的.而泛型也是一样的,这样写class Java_Generics<K,V>,这里边的K和V就象方法中的参数str1和st

java 泛型实例详解(普通泛型、 通配符、 泛型接口)

java 泛型详解(普通泛型. 通配符. 泛型接口) 2013-02-04 19:49:49| 分类: JAVA | 标签:java |举报|字号 订阅 下载LOFTER客户端 JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型. 1.Java泛型 其实Java的泛型就是创建一个用类型作为参数的类.就象我们写类的方法一样,方法是这样的method(String str1,String

java泛型---通配符,泛型嵌套

package generic; import java.util.ArrayList; import java.util.List; /** * ? -->通配符,类型不确定,用于声明 变量|形参 上 * 不能用在: * 1,创建对象 * 2,创建泛型类 .泛型方法.泛型接口上 * */ public class WildcardTest { public static void main(String[] args) { //声明 List<?> list = new ArrayLi

java 泛型详解(普通泛型、 通配符、 泛型接口)

java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型. 1.Java泛型 其 实Java的泛型就是创建一个用类型作为参数的类.就象我们写类的方法一样,方法是这样的method(String str1,String str2 ),方法中参数str1.str2的值是可变的.而泛型也是一样的,这样写class Java_Generics<K

Java泛型中通配符的使用

学习目标 掌握通配符"?" 的使用 掌握受限泛型的设置 掌握泛型与子类继承的限制 匹配任意类型的通配符 在开发中对象的引用传递是最常见的,但是如果在泛型类的操作中,在进行传递的时候泛型类型必须匹配才可以传递.否则是无法传递的. class Info<T>{ private T var ; // 定义泛型变量 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; }

JAVA泛型-通配符

黑马程序员:Java培训.Android培训.iOS培训..Net培训 JAVA泛型-通配符 一.泛型与继承 有如下的继承关系和以它们为类型参数的泛型: public class Holder<T>{ T t; public Holder(){} public Holder(T at){ t = at;} public void set(T at){ t = at;} public T get(){return t; } } //有如下代码: Holder<Apple> apple