内容简介:看一段常见的代码记得以前我们使用的时候都需要强转类型,现在这里居然提示这是不必要的 why?发生了什么?什么时候发生的?我们打开这个方法,如下
看一段常见的代码
记得以前我们使用的时候都需要强转类型,现在这里居然提示这是不必要的 why?发生了什么?什么时候发生的?
我们打开这个方法,如下
@SuppressWarnings("TypeParameterUnusedInFormals")
@Override
public <T extends View> T findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
复制代码
@Nullable
public <T extends View> T findViewById(@IdRes int id) {
return getWindow().findViewById(id);
}
复制代码
以上两段代码分别来自于API25,即对应Android8.0源码中的v7包的AppCompatActivity、Activity
我们再看两段代码,如下
@Override
public View findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
复制代码
@Nullable
public View findViewById(@IdRes int id) {
return getWindow().findViewById(id);
}
复制代码
以上两段代码分别来自于API24,即对应Android7.0源码中的v7包的AppCompatActivity、Activity
Ps:你可以试着把module的依赖的SDK版本和AppCompatActivity的版本降低到24及以下,就会出现需要如下情况
对比两个版本的代码,出现了重点,就是< T extends View > T 代替了原来的View。这种代码就是使用泛型的栗子。
PS:末尾有惊喜
泛型的作用与定义
作用
1.代码写起来比较方便,直接fbi一气呵成(如上栗),不用担心控件的类型转换问题。
再举个栗子,
List list = new ArrayList();
list.add(123);
list.add("123");
int i = (Integer) list.get(1);
System.out.println(i);
复制代码
可在main方法里执行以上代码,编译器并不会报错,但执行的时候会出现类的强制转换错误,这里的创建了ArrayList的实例,即默认为Object,所以无论是123还是“123”都被当初object添加,但是,取出的时候会被自动强转成添加进去的类型,即list.get(1)取出的是String类型,而String类型是不能强转成Integer类型的。
如果我们使用泛型呢
2.代码的运行更加安全,能有效的避免运行时才知道类型转换错误,可以提前发现错误,进行修正。
定义
泛型,即参数化类型,是在JDK1.5之后才开始引入的。所谓参数化类型,是指所操作的数据类型在定义是被指定为一个参数,然后在使用时传入具体的类型。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
使用
上述例子就已有使用,我们可以发现List是个泛型接口,即List 然后看到它的get方法,返回值即传入的类型,E只是个形参,Integer才是实参,所以当我们把泛型设置成Integer的时候,此时的list数据集合的类型被确定,get出来的即为Integer,所以再次添加String类型即报错。
下面列出每个用例的标准类型参数:
• E:元素
• K:键
• N:数字
• T:类型
• V:值
• S、U、V 等:多参数情况中的第 2、3、4 个类型
使用的注意事项:
① 所有泛型声明都有一个类型参数声明部分(由尖括号分隔),泛型方法的该类型参数声明部分在方法返回类型之前,泛型类和接口在直接在名称后面添加了类型参数声明部分(例如)
② 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
③ 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
④ 泛型的参数类型可以使用extends语句,例如。习惯上称为“有界类型”。 这里的限定使用关键字extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现XX接口的类型,或者T是继承了XX类的类型。 <T extends SomeClass & interface1 & interface2 & interface3>
⑤ 泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");
1、如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何 Java 类了。也就是任意类。
2、通配符泛型不单可以向上限制,如<? extends Collection>,还可以向下限制,如<? super Double>,表示类型只能接受Double及其上层父类类型,如Number、Object类型的实例。
泛型类的使用
/泛型类的使用
ClassName<String, String> a = new ClassName<String, String>();
a.doSomething("hello world");
//**************************分割线***********************
//泛型类的定义
static class ClassName<T1, T2> { // 可以任意多个类型变量
public void doSomething(T1 t1) {
System.out.println(t1);
}
}
复制代码
泛型方法的使用
//泛型方法的使用
System.out.println(getMax("hello world","hello world !!!"));
//**********************分割线****************************
//泛型方法的定义
static <T extends Comparable<T>> T getMax(T t1, T t2) {
if (t1.compareTo(t2) > 1) {
return t1;
} else {
return t2;
}
}
复制代码
泛型接口的使用
//泛型接口的使用
InterfaceName<String, String> b = new ConcreteName<String>();
b.doSomething("hello world !!!");
//*********************分割线****************************
//泛型接口的定义
interface InterfaceName<T1, T2> { // 可以任意多个类型变量
public void doSomething(T1 t1);
}
static class ConcreteName<T2> implements InterfaceName<String, T2> {
public void doSomething(String t1) {
System.out.println(t1);
}
}
复制代码
原理(重点)
Java中的泛型是通过类型擦除来实现的。所谓类型擦除,是指通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。
举个栗子
package upupup.fanxing;
import java.util.ArrayList;
/**
* @version: v1.0
* @description: 泛型原理的探究
* @package: upupup.fanxing
* @author: 小小黑
* @date :2018/12/28
*/
public class test01 {
public static void main(String[] args) {
/**
* 证明只生成了一个类,两个实例共享
*/
// 声明一个具体类型为String的ArrayList
ArrayList<String> arrayList1 = new ArrayList<String>();
arrayList1.add("abc");
// 声明一个具体类型为Integer的ArrayList
ArrayList<Integer> arrayList2 = new ArrayList<Integer>();
arrayList2.add(123);
// 结果为true
System.out.println(arrayList1.getClass() == arrayList2.getClass());
/**
* 证明了在编译后,擦除了Integer这个泛型信息,只保留了原始类型
*/
ArrayList<Integer> arrayList3 = new ArrayList<Integer>();
arrayList3.add(1);
try {
arrayList3.getClass().getMethod("add", Object.class).invoke(arrayList3, "asd");
for (int i = 0; i < arrayList3.size(); i++) {
System.out.println(arrayList3.get(i)); // 输出1,asd
}
// NoSuchMethodException:java.util.ArrayList.add(java.lang.Integer
arrayList3.getClass().getMethod("add", Integer.class).invoke(arrayList3, 2);
}catch (Exception e){
e.printStackTrace();
}
}
}
复制代码
这里暴露了一个问题,既然擦除了,那么返回给我们收到的应该是一个object对象,为什么我们能直接得到我们需要的对象?不需要进行强转?原因是Java的泛型除了类型擦除之外,还会自动生成checkcast指令进行强制类型转换
看个例子
public static void main(String[] args) {
List<Integer> a = new ArrayList<Integer>();
a.add(1);
Integer ai = a.get(0);
System.out.println(ai);
}
复制代码
我们来编译一下,看他的字节码,即.class文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package upupup.fanxing;
import java.util.ArrayList;
public class test02 {
public test02() {
}
public static void main(String[] var0) {
ArrayList var1 = new ArrayList();
var1.add(1);
Integer var2 = (Integer)var1.get(0);
System.out.println(var2);
}
}
复制代码
我们干了啥?这和我们直接强转有区别吗?泛型同样需要强转,并不会提高运行效率,但是会降低编程时的错误率,即我们刚开始所说的泛型的好处。
通配符泛型方法和嵌套
举个栗子
package upupup.fanxing;
import java.util.ArrayList;
import java.util.List;
/**
* @version: v1.0
* @description: 通配符泛型方法和嵌套
* @package: upupup.fanxing
* @author: 小小黑
* @date :2018/12/28
*/
public class test03 {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
System.out.println("***************");
// getUperNumber(name);//1
getUperNumber(age);//2
getUperNumber(number);//3
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
public static void getUperNumber(List<? extends Number> data) {
System.out.println("data :" + data.get(0));
}
}
复制代码
通过栗子我们很明显知道,这个通配符是和泛型搭配使用的,因为我们需要得到不同类型的data,如果我们不使用泛型取出的时候就需要逐个进行强转,而使用?通配符就把这个问题解决了,但注意这并不是通配符泛型方法,但我们可以修改一下
//通配符泛型方法的使用
System.out.println("?data :" +getData1(name).get(0));
//***********************分割线****************
//定义通配符泛型方法
public static List<?> getData1(List<?> data) {
return data;
}
复制代码
这里应该很容易就能明白,通配符即适配任一类型,表示未知(单一)的Object类型
嵌套涉及的一些知识
嵌套后如何进行类型擦除?又会产生什么新的问题呢?
类型擦除的规则:
- 擦除后变为Obecjt
- 擦除后变为A
- <? super A>擦除后变为Object
这个规则叫做保留上界,很容易想到这个,但我们未给List设置泛型的时候,即默认为Object就是这个道理
给个栗子看一哈
Number num = new Integer(1); //type mismatch ArrayList<Number> list = new ArrayList<Integer>(); List<? extends Number> list = new ArrayList<Number>(); list.add(new Integer(1)); //error list.add(new Float(1.2f)); //error 复制代码
为什么Number的对象可以由Integer实例化,而ArrayList的对象却不能由ArrayList实例化?list中的<? extends Number>声明其元素是Number或Number的派生类,为什么不能add Integer和Float?
要弄明白上述问题,我们先得了解一哈
1.里氏替换原则
2.逆变与协变用来描述类型转换(type transformation)后的继承关系
3.泛型经历了类型擦除过后的继承关系
里氏替换原则
所有引用基类(父类)的地方必须能透明地使用其子类的对象。
LSP包含以下四层含义:
子类完全拥有父类的方法,且具体子类必须实现父类的抽象方法。
子类中可以增加自己的方法。
当子类覆盖或实现父类的方法时,方法的形参要比父类方法的更为宽松。
当子类覆盖或实现父类的方法时,方法的返回值要比父类更严格。
这里可以想到我们使用的向上造型是不是就是这个原理
逆变和协变
逆变与协变用来描述类型转换(type transformation)后的继承关系,其定义:
如果A、B表示类型,f(⋅)表示类型转换,≤表示继承关系(比如,A≤B表示A是由B派生出来的子类);
f(⋅)是逆变(contravariant)的,当A≤B时有f(B)≤f(A)成立;
f(⋅)是协变(covariant)的,当A≤B时f(A)≤f(B)成立;
f(⋅)是不变(invariant)的,当A≤B时上述两个式子均不成立,即f(A)与f(B)相互之间没有继承关系。
泛型如何变化
令f(A)=ArrayList ,那么f(⋅)时逆变、协变还是不变的呢?如果是逆变,则ArrayList是ArrayList的父类型;如果是协变,则ArrayList是ArrayList的子类型;如果是不变,二者没有相互继承关系。前面我们用ArrayList实例化list的对象错误,则说明泛型是不变的。
以上所述就是小编给大家介绍的《Java语言进阶篇:泛型原理与Android网络应用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- [译] GAN 研究: 生成对抗网络的原理、应用场景及未来
- Rails 4.2.10 发布,开源网络应用框架
- Rails 5.2.0 发布,开源网络应用框架
- Rails 5.2.0 发布,开源网络应用框架
- SEO和渐进式网络应用:展望未来
- Netty 4.1.48.Final 发布,异步事件驱动网络应用框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Masterminds of Programming
Federico Biancuzzi、Chromatic / O'Reilly Media / 2009-03-27 / USD 39.99
Description Masterminds of Programming features exclusive interviews with the creators of several historic and highly influential programming languages. Think along with Adin D. Falkoff (APL), Jame......一起来看看 《Masterminds of Programming》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
URL 编码/解码
URL 编码/解码