android studio 泛型,从findViewById中看泛型的使用

c2ba1bd6d1a6?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

图片来自网络

文/JamFF

01

周末帮人做一个Android小应用,没有使用ButterKnife或者DataBinding,老老实实的findViewByById,竟然提示是个多余的操作。

c2ba1bd6d1a6?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

赶紧点进去看看,果然是通过泛型实现的。

@SuppressWarnings("TypeParameterUnusedInFormals")

@Override

public T findViewById(@IdRes int id) {

return getDelegate().findViewById(id);

}

我这里使用的是appcompat-v7-26.1.0下的AppCompatActivity,

再看下API 26中的android.app.Activity也是用了泛型。

@Nullable

public T findViewById(@IdRes int id) {

return getWindow().findViewById(id);

}

API 25中是这个样子,

@Nullable

public View findViewById(@IdRes int id) {

return getWindow().findViewById(id);

}

想到很久以前没有ButterKnife和DataBinding时,自己一直都是这么做的,

public T $(int id) {

return (T) findViewById(id);

}

当时也有很多像Afinal和xUtils通过运行时注解的方式去解决,但是由于反射影响效率,之后就没有用过。

02

话说回来,这里面泛型是什么意思呢?

上面是泛型方法的一种使用,

在修饰符后返回值前,定义一个继承View的泛型T,当然也可以不继承View,继承的目的有两个,

一是,增加限定,只允许View及其子类接收findViewById返回的结果;

二是,方便使用,直接拥有View的方法,例如下面写法,可以少定义一个变量。

findViewById(R.id.btn).setOnClickListener(this);

再举一个例子,

public void print(T t) {

System.out.println(t);

}

调用该方法时,参数就可以是任意类型了,省去了重载的方法。

这里可能有人说,用Object也一样啊,再看下面一个例子,

public void test() {

String str = test1("object");// 返回为String

String obj = (String) test2("object");// 返回为Object

}

private E test1(E t) {

return t;

}

private Object test2(Object t) {

return t;

}

由此可见,使用泛型的两个好处,

第一,方便,不需要强转,使用Object需要强转;

第二,安全,使用Object强转时,可能出会出现ClassCastException。

那么问题来了,使用泛型,将一个Button类型让ImageView接收,结果会怎么样呢?

ImageView iv = findViewById(R.id.btn);

首先Android Studio很感人啊,直接给出提示,注意这不是泛型的提示,而是IDE给出的。

c2ba1bd6d1a6?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

当然如果你不管它,直接运行,还是会出现了ClassCastException异常的。

03

说到泛型,就必须提Java两个泛型的坑

泛型的类型擦除

简单的理解就是,类型参数只存在于编译期,在运行时,JVM并不知道泛型的存在。

例1:

Class c1 = new ArrayList().getClass();

Class c2 = new ArrayList().getClass();

System.out.println(c1 == c2);

这个打印结果是true

原因就是类型擦除,编译器生成的字节码,在运行期间并不包含泛型的类型信息,运行时两个都是ArrayList类型。

例2:

private T cast(Class clazz, Object obj) {

T t = (T) obj;

return t;

}

调用

Long aLong= cast(Long.class, 4);// 运行报错,不能将Integer转换为Long

第一,在cast方法中T t = (T) obj;运行不会报错,因为在运行期间,泛型擦除,相当于Object t = (Object ) obj;并没有任何作用。

第二,针对上面例子,cast方法不会将Integer强转为Long,返回值的t就是Integer类型,所以将返回值赋给Long类型时,会运行报错;

第三,那么将返回值赋值给Integer,就可以了吗?当然不是,由于泛型的限制,编译时要求返回值为T类型,也就是Long,如果返回Integer,编译期间都不会通过。

Integer integer = cast(Long.class, 4);// 编译报错,返回值为泛型,应该是Long

所以,唯一的办法就是先用Object接收,然后再使用Integer强转。

Object cast = cast(Long.class, 4);

Integer integer = (Integer) cast;

泛型类的定义会影响泛型方法

例:

public class Test {

T value(T t) {

return t;

}

}

private void fun() {

Test test1 = new Test();

// 只能推断为Object

Object o = test1.value(0.1);

Test test2 = new Test<>();

// 可以推断为Double

Double aDouble = test2.value(0.1);

}

如果泛型类中定义的泛型没有被使用,与泛型方法同时使用时,并且该泛型方法不是静态方法时,就会出现上面的问题,这个在Java语言规范中有写到。

To facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure (§4.6) of a parameterized type (§4.5) or the erasure of an array type (§10.1) whose element type is a parameterized type. Such a type is called a raw type.

More precisely, a raw type is defined to be one of:

The reference type that is formed by taking the name of a generic type declaration without an accompanying type argument list.

引用类型是通过使用泛型类型声明的名称,而没有实际的类型参数列表来形成的。(说白了就是定义了泛型T,在调用时却没有使用)

An array type whose element type is a raw type.

一个数组类型,其元素类型为raw type。

A non-static member type of a raw type R that is not inherited from a superclass or superinterface of R.

一种非静态类型的原始类型R,并且它没有继承父类或实现接口。

上面就是简单的介绍一些泛型的优缺点,在日后设计代码的时,可以利用泛型完善代码,简化代码,增加扩展性及安全性。

我是JamFF,希望今天的文章对你有帮助。

END.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值