玉树wap网站建设公司,怎么做淘课网站,什么是网络营销方案,做网站需要学jq吗参考文献#xff1a;https://pingfangx.github.io/java-tutorials/java/generics/types.html
1#xff0c;什么是泛型#xff1f;
Java泛型(generics)是JDK5中引入的一个新特性#xff0c;泛型提供了 编译时类型安全检测机制#xff0c; 该机制允许程序员在编译时检测到…参考文献https://pingfangx.github.io/java-tutorials/java/generics/types.html
1什么是泛型
Java泛型(generics)是JDK5中引入的一个新特性泛型提供了 编译时类型安全检测机制 该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数类型也就是说所操作的数据类型被指定为一个参数。 泛型不存在于JVM虚拟机。
通俗点讲就是将类型参数化数据类型被设置为一个参数在使用时再从外部传入一个数据类型而一旦传入了具体的数据类型后传入变量实参的数据类型如果不匹配编译器就会直接报错。这样提高了代码的类型安全性使你在编译时可以检测到更多错误。
2为什么要使用泛型
泛型在定义类接口和方法时使类型类和接口成为参数。与方法声明中使用的形式参数非常相似类型参数为你提供了一种使用不同输入重复使用相同代码的方法。区别在于形式参数的输入是值而类型参数的输入是类型。 下面来看一个官方的例子 使用非泛型的代码如下 List list new ArrayList();
list.add(hello);
String s (String) list.get(0);//需要强制转换list.add(1);
s (String) list.get(1); //在编译时期没有任何错误提示 在运行时期会报错 使用泛型的代码如下 ListString list new ArrayListString();
list.add(hello);
String s list.get(0); //不需要进行强制转换list.add(1);
s list.get(0); //会在编译期就报错 Required type:String Provided:int 通过上面官方例子我们不难发现与非泛型代码相比使用泛型的代码具有以下优点 1在编译时进行更强的类型检查。Java编译器将强类型检查应用于通用代码如果代码违反类型安全则会发出错误。修复编译时错误比修复运行时错误容易后者可能很难找到。 2消除类型转换。当使用泛型重写时代码不需要强制转换。 3使程序员能够实现通用算法。通过使用泛型程序员可以实现对不同类型的集合进行工作可以自定义并且类型安全且易于阅读的泛型算法。 3泛型类的创建
泛型类是通过类型进行参数化的通用类。
泛型类的定义格式如下
class 类名T1, T2, ..., Tn {private 泛型标识 变量名;}
//例如
class StudentT {private T t;public void set(T t) { this.t t; }public T get() { return t; }}
上面T可以随便定义只要是在中你甚至可以定义为dnadnandn都行注意尽量不要定义为关键字以免引起冲突。
注意类型参数与类型变量的区别
StudentT中的T为类型参数
StudentString中的String为类型变量
常用的类型参数名称是
T - Type类型 最常用
E - 元素或Exception异常
K - Key
V - Value
N - 数字
S,U,V 等第二第三第四个类型
下面看一个简单的例子
泛型类
public class MyListT {private ListT list;public void add(T t){list.add(t);}public T get(int i){return list.get(i);}public int size(){return list.size();}}
不使用泛型的类
public class MyList1 {private ListObject list;public void add(Object t){list.add(t);}public Object get(int i){return list.get(i);}public int size(){return list.size();}
}
在使用时 //泛型类的使用MyListString myList new MyList();myList.add(aaa);//myList.add(1); 在编译时就报错for (int i 0; i myList.size(); i) {String s1 myList.get(i);System.out.println(s1s1);}//非泛型类的使用MyList1 myList1 new MyList1();myList1.add(11);//不会报错myList1.add(111);for (int i 0; i myList1.size(); i) {String s1 (String) myList1.get(i);//1需要强转 2在运行时会报错System.out.println(s1s1);}
泛型类还可以传入多个泛型参数下面看一个 两个参数的泛型类
public class PeopleK,V {private K name;private V achievement;public People(K name, V achievement) {this.name name;this.achievement achievement;}public void setName(K name) {this.name name;}public void setAchievement(V achievement) {this.achievement achievement;}public K getName() {return name;}public V getAchievement() {return achievement;}
}
PeopleString,Integer p1 new People(张三,98);
PeopleString,Integer p2 new People(赵武,99);
// PeopleString,Integer p3 new People(赵武,100);//编译阶段报错
需要注意的是泛型类中的静态方法和静态变量不可以使用泛型类所声明的类型参数例如
public class TestT {private static T name;//编译时报错com.yuanzhen.Test.this cannot be referenced from a static contextpublic static T getName(){//编译时报错com.yuanzhen.Test.this cannot be referenced from a static contextreturn name;}
}原因是泛型在对象创建时才知道是什么类型但是静态方法属于类调用getName方法实际调用的Test类的方法而类在编译阶段就存在了所以虚拟机根本不知道方法中引用的泛型是什么类型 初始化时对象创建的代码执行先后顺序是static的部分然后才是构造函数等等所以在对象初始化之前static的部分已经执行了如果你在静态部分引用的泛型那么毫无疑问虚拟机根本不知道是什么东西
4泛型接口的创建
泛型接口的定义和泛型类的定义一样区别就在于一个是接口需要实现各个接口方法一个是类需要实现对应的抽象方法。
泛型接口的定义格式如下
public interface 接口名类型参数 {...
}
//例如
public interface IPeopleT {T getName();void setName(T t);
}
泛型的具体使用方式
①直接在实现类中指定泛型的具体类型
public class StudentString implements IPeopleString{Overridepublic String getName() {return null;}Overridepublic void setName(String string) {}
}
②在实现类中继续使用泛型在实例化实现类对象的时候指定泛型的具体类型
public class TeacherT implements IPeopleT{Overridepublic T getName() {return null;}Overridepublic void setName(T t) {}
}
TeacherString teacher new Teacher();
teacher.setName(张三);
③在接口继承接口中指定泛型的具体类型。
public interface ITeacherString extends IPeopleString{}
5泛型方法的创建
泛型方法是指引入自己的类型参数的方法。这类似于声明一个泛型方法但类型参数的范围仅限于声明它的方法。允许使用静态和非静态的泛型方法也允许使用泛型类构造函数。 泛型方法的语法包括类型参数列表在尖括号内该列表出现在方法的返回类型之前。对于静态泛型方法类型参数部分必须出现在方法的返回类型之前。 泛型方法的格式如下 public 类型参数1类型参数2... 返回类型 方法名类型参数1 变量名1,类型参数2 变量名2 ... {...
}
//例如
public class Doctor {/*** 泛型方法*/public T void doWork(T t){}
} 注意只有在方法声明中声明了的方法才是泛型方法 下面看个例子 public class DoctorT {private T name;/** 不是泛型方法 只是普通方法* */public Doctor(T name) {this.name name;}/** 不是泛型方法 只是普通方法* */public void setName(T name) {this.name name;}/** 不是泛型方法 只是普通方法* */public T getName() {return name;}/** 泛型方法 因为它定义了自己的T* */public T void doWork(T t){}
} 调用泛型方法 DoctorString doctor new Doctor(张三);
doctor.StringdoWork(看病);//调用泛型方法的完整语法
doctor.doWork(看病);//因为类型推断所以可以省略String 类型推断上文中可以看出因为类型推断可以省略那么什么是类型推断呢 类型推断(Type Inference)是指 Java 编译器能查看每个方法的调用和相应声明以确定调用合适的类型参数(Type Argument)或参数。推断算法决定参数的类型如果可用则指定被赋值的类型或返回的类型。最后推断算法试图在一起工作的所有参数中找到最具体的类型。 引入类型推断的泛型方法能够让你像调用普通方法一样调用泛型方法而不需要在尖括号中指定类型。 5限定类型参数 有时你可能想限制可以在参数化类型中用作类型参数的类型。例如对数字进行操作的方法可能只希望接受 Number 或其子类的实例。这就是限定类型参数的用途。 要声明一个限定的类型参数请列出类型参数的名称然后列出 extends 关键字然后列出其上限在本示例中为 Number 。请注意在这种情况下 extends 通常用于表示“扩展”如在类中或“实现” 如在接口中。 限定类型参数格式 T extends B1 //单个限定
T extends B1 B2 B3 //多重限定 举例如下 public class TeacherT extends Number{private T age;public T getAge() {return age;}public void setAge(T t) {}
} TeacherString teacher1 new Teacher();//报错 因为已经限定了类型必须是Number及其子类
teacher1.setAge(张三);
//正确用法
TeacherInteger teacher2 new Teacher();
teacher2.setAge(20); 6通配符
在通用代码中称为通配符的问号 ? 表示未知类型。通配符可以在多种情况下使用作为参数字 段或局部变量的类型有时作为返回类型尽管更具体的做法是更好的编程习惯。通配符从不用作泛 型方法调用泛型类实例创建或超类型的类型参数。 ①上限通配符
格式如下
? extends T
//例如
? extends Foo
//其中Foo可以是任何类型匹配Foo和Foo的任何子类型
下面来看一个具体的使用案例
public class Fruit {Overridepublic String toString() {return 水果;}
}
public class Apple extends Fruit {Overridepublic String toString() {return 苹果;}
}
ArrayListFruit fruits new ArrayListApple();//这样会在编译期报错
这样为什么会报错呢Java不是可以将子类对象赋值给一个父类对象的引用吗
下来再来看一下这段代码
public class Banana extends Fruit {Overridepublic String toString() {return 香蕉;}
}
ArrayListApple apples new ArrayListApple();
Apple apple new Apple();
apples.add(apple);
//----------------上面的代码不会报错 是正常逻辑------------
ArrayListFruit fruits apples;//假如这行代码在编译期不报错的话
fruits.add(new Banana());
Apple a fruits.get(1);//这行代码在运行期间就会报类型转换异常的错误
所以泛型是不支持这种向上转型的。
但是如果我们一定要这么写呢也不是不可以用通配符就可以做到
ArrayList? extends Fruit fruits new ArrayListApple();//编译期不会报错
Apple apple new Apple();
fruits.add(apple);//但是在添加的时候会在编译期报错
大家想一想为什么会在添加的时候会在编译期报错呢
因为如果放开的话我还是同样的可以添加香蕉苹果等子类这样在运行期间就可能会出现更大的错误所以编译器直接就不让你添加了这样就不会有问题了。
但是这样做有什么意义呢
当你只想让用户往外取值不想让用户进行写入时? extends Fruit的意义就体现出来了简而言之就是只能读取不能写入
②下限通配符
格式如下
? super T
//例如
? supper Zoo
//其中Zoo可以是任何类型匹配Zoo和Zoo的任何超类
下面来看一个例子
ArrayListApple fruits new ArrayListFruit();//会在编译期报错
Apple apple new Apple();
fruits.add(apple);
为什么会在编译期报错呢同上限通配符一样的道理泛型也不支持这种转型
那么需要怎么做呢请看下面
ArrayList? super Apple fruits new ArrayListFruit();//不报错 正常运行
Apple apple new Apple();
fruits.add(apple);
Object object fruits.get(0);
使用下限通配符为什么可以添加呢 因为往里面添加的都是apple的父类其归根结底都会用一个最终的父类表示所以不会有问题。但是往外读的时候我不知道是苹果还是水果所以不建议读取虽然可以用Object接收。
简而言之与上限通配符相反下线通配符只能写入不能读取
③通配符使用准则
在开发中我们应该在什么时候使用上限通配符和下限通配符呢 “ 输入 ” 变量输入变量将数据提供给代码。一个具有两个参数的复制方法 copy(src, dest) 。 src参数提供要复制的数据因此它是输入参数。 “ 输出 ” 变量输出变量保存要在其它地方使用的数据。在复制示例 copy(src, dest) 中dest参数接 受数据因此它是输出参数。 通配符准则
使用上限通配符定义输入变量使用 extends 关键字。
使用下限通配符定义输出变量使用 super 关键字。
如果可以使用 Object 类中定义的方法访问输入变量请使用无界通配符 ? 。
如果代码需要同时使用输入和输出变量来访问变量则不要使用通配符。 这些准则不适用于方法的返回类型。应该避免使用通配符作为返回类型 7类型擦除
Java语言引入了泛型以在编译时提供更严格的类型检查并支持泛型编程。 为了实现泛型Java编译器将类型擦除应用于 1如果类型参数不受限制则将通用类型中的所有类型参数替换为其边界上下限或 Object 。因此产生的字节码仅包含普通的类接口和方法。 2必要时插入类型转换以保持类型安全。 3生成桥接方法以在扩展的泛型类型中保留多态。 类型擦除可确保不会为参数化类型创建新的类因此泛型不会产生运行时开销。 在类型擦除过程中Java编译器将擦除所有类型参数如果类型参数是有界的则将每个参数替换为其第一个边界如果类型参数是无界的则将其替换为 Object 。 下面来看几个例子
public class NodeT {private T data;private NodeT next;public Node(T data, NodeT next) {this.data data;this.next next;}public T getData() { return data; }}
由于类型参数T是无界的因此Java编译器将其替换为 Object public class Node {private Object data;private Node next;public Node(Object data, Node next) {this.data data;this.next next;}public Object getData() { return data; }
} 在下面的示例中通用Node类使用限定类型参数 public class NodeT extends ComparableT {private T data;private NodeT next;public Node(T data, NodeT next) {this.data data;this.next next;}public T getData() { return data; }
} Java编译器将绑定类型参数T替换为第一个绑定类 Comparable public class Node {private Comparable data;private Node next;public Node(Comparable data, Node next) {this.data data;this.next next;}public Comparable getData() { return data; }
} 8对泛型的限制
①无法实例化具有基本类型的泛型类型
请看下面例子
class PairK, V {private K key;private V value;public Pair(K key, V value) {this.key key;this.value value;}
}
创建对对象时不能用基本类型替换类型参数K或V
Pairint, char p new Pair(8, a);//编译错误
你只能将非基本类型替换为类型参数K和V
PairInteger, Character p new Pair(8, a); ②无法创建类型参数的实例
例如以下代码会导致编译时错误
public static E void append(ListE list) {E elem new E(); //编译期错误list.add(elem);
}
解决方法是可以通过反射创建类型参数的对象
public static E void append(ListE list, ClassE cls) throws Exception {E elem cls.newInstance(); // OKlist.add(elem);
} ③无法声明类型为类型参数的静态字段
类的静态字段是该类的所有非静态对象共享的类级别变量。因此不允许使用类型参数的静态字段
public class MobileDeviceT {private static T os;
} ④无法将Casts或instanceof与参数化类型一起使用
因为Java编译器会擦除通用代码中的所有类型参数所以你无法验证在运行时使用的是通用类型的参数 化类型 public static E void rtti(ListE list) {if (list instanceof ArrayListInteger) { // 编译器错误// ...}
} ⑤无法创建参数化类型的数组
ListInteger[] arrayOfLists new ListInteger[2];//编译期报错 ⑥无法创建捕获或抛出参数化类型的对象
泛型类不能直接或间接扩展 Throwable 类。 class MathExceptionT extends Exception { /* ... */ } // 编译期错误
class QueueFullExceptionT extends Throwable { /* ... */ }// 编译器错误 方法无法捕获类型参数的实例 public static T extends Exception, J void execute(ListJ jobs) {try {for (J job : jobs)} catch (T e) { //编译期错误}
} 但是你可以在 throws 子句中使用类型参数 class ParserT extends Exception {public void parse(File file) throws T { // OK}
} ⑦无法重载每个重载的形式参数类型都擦除为相同原始raw类型的方法
一个类不能有两个重载的方法这些方法在类型擦除后将具有相同的签名。 public class Example {public void print(SetString strSet) { }public void print(SetInteger intSet) { }
} 9在安卓中的使用
那么在了解了泛型的知识之后我们在安卓开发中到底用到了哪些泛型呢
①网络请求
最常用的地方就是解析http请求下来的json数据。因为http请求下来的数据我们不知道需要转换成什么类型只有在调用的地方才知道所以我们采用泛型。
先定义一个回调接口
public interface CallBackT {void onError(Exception e);void onSuccess(T t);
}
在实际请求中传入匿名内部类 OmniHttp.get(getHOST() /app-wn/login).syncRequest(true).execute(new CallBackLoginBean() {Overridepublic void onError(ApiException e) {}Overridepublic void onSuccess(LoginBean loginBean) {}
});
这只是简单的写了一个小例子可以尝试在自己写一下
②findViewById() /*** FindViewById的源码泛型封装减少强转代码*/public T extends View T findViewById(IdRes int id) {return getDelegate().findViewById(id);}//使用前
ImageView item (ImageView)findViewById(R.id.item);
//使用后
ImageView item findViewById(R.id.item);
③BaseAdapter实现封装的Adapter
有时间补充
④MVP架构
有时间补充
10总结
花了一天时间整理了下泛型的相关知识点在android中的应用后续还会继续补充。