从List学习JAVA泛型

泛型的优点

  1. 适用于多种数据类型执行相同的代码
  2. 类型安全,编译期对参数类型判断,增加安全性
  3. 取消强制类型转换

泛型分类

从List和ArrayList基本可以学习到泛型所有的知识点。泛型分为泛型类,泛型接口,泛型方法。先上一些源码:

public interface List<E> extends Collection<E> {
    E get(int index);
    void add(int index, E element);}
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
        @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }
    
        public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }    
}

泛型接口

List是个接口对象,List 中E也就是所谓的泛型,我们都知道我们初始化List都会声明List,List等,而E也就是泛指所有类型。当然是指继承Object对象的所有类型,这也是为何List无法传入基本数据类型。

泛型类

ArrayList也就是泛型类,其实原理与接口类似。此处值得注意的是:ArrayList指定的泛型与父类AbstractList及List指定的泛型对象都是E,也就是必须为同一类型。当然可以设置多个泛型。可以参考下HashMap。

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    }

泛型方法

可以参考ArrayList.toArray方法。
首先声明泛型,其次返回值为T的数组,传入值也为T的数组

进阶

不知道大家是否使用过MVP的框架,简单代码如下。

public class MVPBaseActivity<V extends BaseView, T extends BasePresenterImpl<V>> 
       extends Activity  {
        }

从代码中可以看出,我希望限制泛型为BaseView的字类型,则使用V extends BaseView。其中,extends不是类继承的extends,两者并无关系,BaseView可以为类,也可以为接口,类继承的extends 无法继承接口,正可以说明两者无关系。

通配符 ?

无边界通配符

先看例子,我们经常使用HashMap,初始化HashMap时,可能会有如下定义:

HashMap<String, ?> map = new HashMap<>();

当我们不确定hashMap中value的 类型时,我们可以采用通配符?代替具体类型。通配符?只能用来指定泛型,其他无法使用。

通配符的限定

extends

举例就比较好理解:

HashMap<String, ? extends Number> map = new HashMap<>();

限制通配符只能为Number的子类型

super

通过extends,可以理解super,也就是限制其为某个特定的父类。仍然举个例子,限制value为String的父类

HashMap<String, ? super String> map = new HashMap<>();

又一个很绕弯的理解:假设三个类Plant, Fruit,Apple三个类,依次继承关系。Fruit extends Plant, Apple extends Fruit.现有代码:

List<? super Fruit> list;
list = new ArrayList<Fruit>();
list = new ArrayList<Plant>();
list = new ArrayList<Apple>(); // 此行编译报错,比较好理解。因为通配符要求是Fruit的父类,而Apple不是Fruit的父类。
---------------------------------------------------------------------------
list = new ArrayList<Fruit>();
list.add(new Plant()); //此行编译报错
list.add(new Fruit());
list.add(new Apple());

明明要求是Fruit的父类,而add Plant的时候却会报错。原因:<? super Fruit>要求为Fruit的父类,也就是说是Fruit或者Plant,Apple是Fruit与Plant的子类,可添加,无疑。Fruit是Fruit和Plant的子类,故也可以添加。(此处子类是泛指)。而Plant不一定是Fruit的子类,故编译期无法识别,而显示报错!

泛型原理

泛型是JDK的一种语法糖,在JVM层面是没有泛型的。所以其实就是在编译期间,将泛型转为了实际使用的类,并且也会做类型检查,如果类型不符合要求,则会编译报错。在转成字节码时,会有checkcast,将泛型转为指定类型

public class Generic<T> {

    private T object;

    public Generic(T object) {
        this.object = object;
    }

    private T getObject() {
        return this.object;
    }

    public static void main(String[] args) {
        Generic<String> generic = new Generic<>("generic paradigm");
        System.out.println(generic.getObject().toString());
    }
}
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #3                  // class com/roc/java/common/Generic
         3: dup
         4: ldc           #4                  // String generic paradigm
         6: invokespecial #5                  // Method "<init>":(Ljava/lang/Object;)V
         9: astore_1
        10: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        13: aload_1
        //getObject时,会有checkcast,将泛型转为String
        14: invokespecial #7                  // Method getObject:()Ljava/lang/Object;
        17: checkcast     #8                  // class java/lang/String
        20: invokevirtual #9                  // Method java/lang/String.toString:()Ljava/lang/String;
        23: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        26: return

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值