引进
看一段常见的代码
记住曾经咱们运用的时分都需求强转类型,现在这儿竟然提示这是不必要的 why?发作了什么?什么时分发作的?
咱们翻开这个办法,如下
@SuppressWarnings("TypeParameterUnusedInFormals")
@Override
publicT findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
@Nullable
publicT 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之后才开端引公主嫁到-Java言语进阶篇:泛型原理与Android网络使用进的。所谓参数化类型,是指所操作的数据类型在界说是被指定为一个参数,然后在运用时传入详细的类型。这种参数类型能够用在类、接口和办法的创建中,别离称为泛型类、泛型接口和泛型办法。
运用
上述比方就已有运用,咱们能够发现List是个泛型接口,即List
下面列出每个用例的规范类型参数:
• E:元素
• K:键
• N:数字
• T:类型
• V:值
• S、U、V 等:多参数状况中的第 2、3、4 个类型
运用的留意事项:
① 一切泛型声明都有一个类型参数声明部分(由尖括号分隔),泛型办法的该类型参数声明部分在办法回来类型之前,泛型类和接口在直接在称号后边增加了类型参数声明部分(例如
② 每一个类型参数声明部分包含一个或多个类型参数公主嫁到-Java言语进阶篇:泛型原理与Android网络使用,参数间用逗号离隔。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型称号的标识符。
③ 类型参数能被用来声明回来值类型,而且能作为泛型办法得到的实践参数类型的占位符。 泛型办法体的声明和其他办法相同。留意类型参数只能代表引证型类型,不能是原始类型(像int,double,char的等)。
④ 泛型的参数类型能够运用extends句子,例如
⑤ 泛型的参数类型还能够是通配符类型。例如Class
1、假如只指定了
2、通配符泛型不单能够向上约束,如
泛型类的运用
//泛型类的运用
ClassNamea = new ClassName ();
a.doSomething("hello world");
//**************************分割线***********************
//泛型类的界说
static class ClassName{ // 能够恣意多个类型变量
public void doSomething(T1 t1) {
System.out.println(t1);
}
}
泛型办法的运用
//泛型办法的运用
System.out.println(getMax("hello world","hello world !!!"));
//**********************分割线********披头士********************
//泛型办法的界说
static> T getMax(T t1, T t2) {
if (t1.compareTo(t2) > 1) {
return t1;
} else {
return t2;
}
}
泛型接口的运用
//泛型接口的运用
InterfaceNameb = new ConcreteName ();
b.doSomething("hello world !!!");
//*********************分割线****************************
//泛型接口的界说
interface InterfaceName{ // 能够恣意多个类型变量
public void doSomething(T1 t1);
}
static class ConcreteNameimplements InterfaceName {
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
ArrayListarrayList1 = new ArrayList ();
arrayList1.add("abc");
// 声明一个详细类型为Integer的ArrayList
ArrayListarrayList2 = new ArrayList ();
arrayList2.add(123);
// 成果为true
System.out.println(arrayList1.getClass() == arrayList2.getClass());
/**
* 证明了在编译后,擦除了Integer这个泛型信息,只保存了原始类型
*/
ArrayListarrayList3 = new ArrayList ();
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) {
Lista = new ArrayList ();
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;
pub公主嫁到-Java言语进阶篇:泛型原理与Android网络使用lic 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) {
Listname = new ArrayList ();
Listage = new ArrayList ();
Listnumber = new ArrayList ();
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
System.out.println("data :" + data.get(0));
}
public static void getUperNumber(List
System.out.println("data :" + data.get(0));
}
}
经过栗子咱们很明显知道,这个通配符是和泛型调配运用的,由于咱们需求得到不同类型的data,假如咱们不运用泛型取出的时分就需求逐一进行强转,而运用?通配符就把这个问题解决了,但留意这并不是通配符泛型办法,但咱们能够批改一下
//通配符泛型办法的运用
System.out.println("?data :" +getData1(name).get(0));
//***********************分割线****************
//界说通配符泛型办法
public static List
return data;
}
这儿应该很简单就能理解,通配符即适配任一类型,表明不知道(单一)的Object类型
嵌套触及的一些常识
嵌套后怎么进行类型擦除?又会发生什么新的问题呢?
类型擦除的规矩:
擦除后变为Obecjt - <? super A>擦除后变为Object
这个规矩叫做保存上界,很简单想到这个,但咱们未给List设置泛型的时分,即默以为Object便是这个道理
给个栗子看一哈
Number num = new Integer(1);
//type mismatch
ArrayListlist = new ArrayList ();
List();
list.add(new Integer(1)); //error
list.add(new Float(1.2f)); //error
为什么Number的目标能够由Integer实例化,而ArrayList
要弄理解上述问题,咱们先得了解一哈
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)彼此之间没有承继联系。
泛型怎么改变
所以这个时分咱们需求经过嵌套来完成,即
这两个是彻底没有问题的,再回到咱们的栗子,咱们只需批改第二种super来完成增加,首要假如咱们运用extends,如上所示,咱们能够增加Number的子类,但详细增加哪一种?无法辨认,然后咱们运用super,如上所示,咱们增加Number的父类,咱们能够直接运用Object,就能直接增加了。
这个比方结合咱们运用通配符的比方,总结一下便是
要从泛型类取数据时,用extends
要往泛型类写数据时,用super
Android上泛型的使用
1.泛型类:网络恳求回来的成果类封装、适配器的基类封装等
2.泛型办法:网络恳求的json转存目标的办法等
3.泛型接口:这个和办法相似,用的少
最终
假如你看到了这儿,觉公主嫁到-Java言语进阶篇:泛型原理与Android网络使用得文章写得不错就给个赞呗?假如你觉得那里值得改善的,请给我留言。一定会仔细查询,批改缺乏。谢谢。
最终针对Android程序员,除了上面的常识系统,我这边给我们整理了一些材料,其间共享内容包含不限于高档UI、功能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技能;期望能协助到我们,也节约我们在网上查找材料的时刻来学习,也能够共享动态给身边老友一同学习!更多材料能够私信找我
为什么某些人会一向比你优异,是由于他自身就很优异还一向在继续尽力变得更优异,而你是不是还在满足于现状心里在窃喜!期望读到这的您能转发共享、点个小赞+重视,今后还会更新技能干货,谢谢您的支撑!
转发共享+重视,每天获取最有用的常识点
Android架构师之路很绵长,一同共勉吧!