设为首页 | 加入收藏

欢迎访问湖南幸运赛车开奖直播_幸运赛车开奖视频直播_幸运赛车开奖直播

中心动态 >> 公主嫁到-Java言语进阶篇:泛型原理与Android网络使用

引进

看一段常见的代码

记住曾经咱们运用的时分都需求强转类型,现在这儿竟然提示这是不必要的 why?发作了什么?什么时分发作的?

咱们翻开这个办法,如下

 @SuppressWarnings("TypeParameterUnusedInFormals")
@Override
public T findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
@Nullable
public 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之后才开端引公主嫁到-Java言语进阶篇:泛型原理与Android网络使用进的。所谓参数化类型,是指所操作的数据类型在界说是被指定为一个参数,然后在运用时传入详细的类型。这种参数类型能够用在类、接口和办法的创建中,别离称为泛型类、泛型接口和泛型办法。

运用

上述比方就已有运用,咱们能够发现List是个泛型接口,即List 然后看到它的get办法,回来值即传入的类型,E仅仅个形参,Integer才是实参,所以当咱们把泛型设置成Integer的时分,此刻的list数据调集的类型被确认,get出来的即为Integer,所以再次增加String类型即报错。

下面列出每个用例的规范类型参数:

• E:元素

• K:键

• N:数字

• T:类型

• V:值

• S、U、V 等:多参数状况中的第 2、3、4 个类型

运用的留意事项:

① 一切泛型声明都有一个类型参数声明部分(由尖括号分隔),泛型办法的该类型参数声明部分在办法回来类型之前,泛型类和接口在直接在称号后边增加了类型参数声明部分(例如

② 每一个类型参数声明部分包含一个或多个类型参数公主嫁到-Java言语进阶篇:泛型原理与Android网络使用,参数间用逗号离隔。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型称号的标识符。

③ 类型参数能被用来声明回来值类型,而且能作为泛型办法得到的实践参数类型的占位符。 泛型办法体的声明和其他办法相同。留意类型参数只能代表引证型类型,不能是原始类型(像int,double,char的等)。

④ 泛型的参数类型能够运用extends句子,例如。习惯上称为“有界类型”。 这儿的约束运用关键字extends,后边能够是类也能够是接口。但这儿的extends现已不是承继的意义了,应该理解为T类型是完成XX接口的类型,或许T是承继了XX类的类型。

⑤ 泛型的参数类型还能够是通配符类型。例如Class

1、假如只指定了

2、通配符泛型不单能够向上约束,如

泛型类的运用

 //泛型类的运用
ClassName a = 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;
}
}

泛型接口的运用

 //泛型接口的运用
InterfaceName b = new ConcreteName();
b.doSomething("hello world !!!");
//*********************分割线****************************
//泛型接口的界说
interface InterfaceName { // 能够恣意多个类型变量
public void doSomething(T1 t1);
}
static class ConcreteName implements 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
ArrayList arrayList1 = new ArrayList();
arrayList1.add("abc");
// 声明一个详细类型为Integer的ArrayList
ArrayList arrayList2 = new ArrayList();
arrayList2.add(123);
// 成果为true
System.out.println(arrayList1.getClass() == arrayList2.getClass());
/**
* 证明了在编译后,擦除了Integer这个泛型信息,只保存了原始类型
*/
ArrayList arrayList3 = 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) {
List a = 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) {
List name = new ArrayList();
List age = new ArrayList();
List number = 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
ArrayList list = new ArrayList();
List();
list.add(new Integer(1)); //error
list.add(new Float(1.2f)); //error

为什么Number的目标能够由Integer实例化,而ArrayList的目标却不能由ArrayList实例化?list中的

要弄理解上述问题,咱们先得了解一哈

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的目标过错,则阐明泛型是不变的。

所以这个时分咱们需求经过嵌套来完成,即

();

();

这两个是彻底没有问题的,再回到咱们的栗子,咱们只需批改第二种super来完成增加,首要假如咱们运用extends,如上所示,咱们能够增加Number的子类,但详细增加哪一种?无法辨认,然后咱们运用super,如上所示,咱们增加Number的父类,咱们能够直接运用Object,就能直接增加了。

这个比方结合咱们运用通配符的比方,总结一下便是

要从泛型类取数据时,用extends

要往泛型类写数据时,用super

Android上泛型的使用

1.泛型类:网络恳求回来的成果类封装、适配器的基类封装等

2.泛型办法:网络恳求的json转存目标的办法等

3.泛型接口:这个和办法相似,用的少

最终

假如你看到了这儿,觉公主嫁到-Java言语进阶篇:泛型原理与Android网络使用得文章写得不错就给个赞呗?假如你觉得那里值得改善的,请给我留言。一定会仔细查询,批改缺乏。谢谢。

最终针对Android程序员,除了上面的常识系统,我这边给我们整理了一些材料,其间共享内容包含不限于高档UI、功能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技能;期望能协助到我们,也节约我们在网上查找材料的时刻来学习,也能够共享动态给身边老友一同学习!更多材料能够私信找我

为什么某些人会一向比你优异,是由于他自身就很优异还一向在继续尽力变得更优异,而你是不是还在满足于现状心里在窃喜!期望读到这的您能转发共享点个小赞+重视,今后还会更新技能干货,谢谢您的支撑!

转发共享+重视,每天获取最有用的常识点

Android架构师之路很绵长,一同共勉吧!



上一条      下一条
返回顶部