内容简介:看一段常见的代码记得以前我们使用的时候都需要强转类型,现在这里居然提示这是不必要的 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 发布,异步事件驱动网络应用框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Pragmatic Programmer
Andrew Hunt、David Thomas / Addison-Wesley Professional / 1999-10-30 / USD 49.99
本书直击编程陈地,穿过了软件开发中日益增长的规范和技术藩篱,对核心过程进行了审视――即根据需求,创建用户乐于接受的、可工作和易维护的代码。本书包含的内容从个人责任到职业发展,直至保持代码灵活和易于改编重用的架构技术。从本书中将学到防止软件变质、消除复制知识的陷阱、编写灵活、动态和易适应的代码、避免出现相同的设计、用契约、断言和异常对代码进行防护等内容。一起来看看 《The Pragmatic Programmer》 这本书的介绍吧!