java中的泛型
文章目录
java中的泛型
我们先思考一下:为什么需要泛型?
我们用一个集合进行举例:
public calss Generic {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
//添加数据
arrayList.add(new Dog("大黄"));
arrayList.add(new Dog("旺财"));
//遍历集合,输出
for(Object obj: arrayList) {
Dog dog = (Dog) obj;
System.out.println(dog.getName());
}
}
}
class Dog {
String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
在上面的代码中,我们遍历集合并输出,需要将 Object 类型做一个类型转换,转换成 Dog 类型,这样才可以进行集合中元素的数据输出。但是,我们思考一下就不难发现,如果我们在集合中添加了一个不是 Dog 类型的数据呢?这样一想是不是就觉得,集合的遍历与输出马上就变得复杂了。
总结: 使用传统方式的缺点
- 不能对加入到集合的数据类型进行约束(这样的操作是很不安全的)
- 遍历的时候,需要进行类型的转换,如果集合中的数据量较大,对效率有一定的影响。
既然有这个缺点,那我们怎么去解决呢?这就引出了这篇文章的内容 泛型 。没错,使用泛型可以解决以上的问题。
泛型的使用
1 快速入门
public calss Generic {
public static void main(String[] args) {
//表示存放到 ArrayList 集合中的元素是Dog类型
ArrayList<Dog> arrayList = new ArrayList<Dog>();
//添加数据
arrayList.add(new Dog("大黄"));
arrayList.add(new Dog("旺财"));
//Dog 类型之外的数据无法加入
//如果编译器发现添加的类型,不满足要求,就会报错
//arrayList.add("字符串");报错
//遍历集合,输出
//在遍历的时候,可以直接取出 Dog 类型而不是 Objec
for(Dog dog: arrayList) {
System.out.println(dog.getName());
}
}
}
class Dog {
String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
2 泛型的介绍
(1)使用泛型的好处
- 编译时,会检查添加元素的类型,提高了安全性。
- 减少了类型转换的次数,提高了效率。
(2)泛型的理解
- 泛型又称参数化类型,是 JDK 5.0 出现的新特性,解决数据类型的安全性问题。
- 在类声明或实例化时,只要指定好需要的具体类型即可。
- Java 泛型可以保证,如果程序在编译时没有发生警告,运行时就不会产生
ClassCastException
异常。同时,代码更加简洁。 - 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
public calss Generic {
public static void main(String[] args) {
//相当与 E = String
//把 Dog 类中的 E 全部替换成 String
//就是这条语句的效果
Dog<String> dog1 = new Dog<String>();
//相当与 E = String
//把 Dog 类中的 E 全部替换成 Integer
//就是这条语句的效果
Dog<Integer> dog2 = new Dog<Integer>();
}
}
class Dog<E> {
//属性使用泛型
E name;
//构造器使用泛型
public Dog(E name) {
this.name = name;
}
//方法的返回值使用泛型
public E getName() {
return this.name;
}
}
(3)泛型的语法
-
泛型可以在类与接口中使用:
interface 接口名<T> {}
和class 类名<K,V> {}
- 其中,T、K、V 不代表值,而是表示类型。
- 原则上,任意字母都可以。常用 T 表示,是 Type 的缩写。
-
泛型的实例化:要在类名后面指定类型参数的值(类型):
List<String> list = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
(4)泛型使用的注意事项
-
interface 接口名<T> {}
和class 类名<K,V> {}
- T、K、V 等只能是引用类型,不能是基本数据类型
- 比如:
List<Integer> list = new ArrayList<Integer>();
可以通过没有问题,Integer 是引用类型。 - 但是:
List<int> list2 = new ArrayList<int>();
这条语句是错误的,因为 int 为基本数据类型,不是引用类型。
-
在给泛型指定具体的类型之后,可以传入该类型或者其子类型。
public class Test {
public static void main(String[] args) {
//指定泛型为 A
ArrayList<A> list = new ArrayList<A>();
//添加的类型只能是 A 类 及 其子类
list.add(A);
//因为 B 类继承了A,所以B类也可以存储在集合 list 中
list.add(B);
}
}
class A {}
class B extends A {}
-
泛型的使用形式
- 普通形式:
List<Integer> list1 = new ArrayList<Integer>();
- 简写形式:
List<Integer> list2 = new ArrayList<>();
这种形式下,编译器会根据我们前的内容自动的去推断我们后方的<>中的内容,在实际使用中,我们常用这种形式。
- 普通形式:
-
默认形式下:
ArrarList list = new ArrayList();
在这种形式下,编译器会自动将泛型指定为 Object 类型。
3 自定义泛型
(1)自定义泛型类
基本语法:
//泛型类
class 类名<T, R, ...> {//...表示可以有多个泛型
// T, R, M 称为泛型的标识符, 一般是单个大写字母
成员
}
//泛型接口
interface 接口名<T, R, ...> {}
注意事项:
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认为 Object。
public class Test {
public static void main(String[] args) {
//创建对象不指定泛型,默认为Object
Student strdent = new Student();
//没有指定泛型,相当于
//Student<Object> student = new Student();
}
}
//泛型类
class Student<T, R> {
//属性使用泛型
T t;
R r;
//方法使用泛型
public T getT() {
return t;
}
//数组使用泛型,不能进行初始化
T[] arr1;
//错误的语句如下:
//T[] arr2 = new T[2];
//因为,泛型是在创建对象时才确定的
//我们初始化数组,泛型是什么类型还没有确定下来
//所以我们无法为其分配内存空间
//java中数据类型所占的字节数是不同的
//比如 int 4个字节
//double 8个字节
//静态方法不能使用类的泛型
/* 下面语句是错误的
static R f1() {
return r;
}
因为,泛型是在创建对象时指定的
而静态属性、静态方法,实在类加载进行初始化的
也就是说,在泛型还没有指定的时候
我们的静态成员就会先进行初始化
*/
}
(2)自定义泛型接口
基本语法: interface 接口名<T, R, ...> {}
注意事项:
- 接口中,静态成员不能使用泛型(和泛型类一样)
- 泛型接口的类型,在继承接口或者实现接口时确定
- 没有指定泛型类型,默认为Object
public class Test {
public static void main(String[] args) {
}
}
//泛型接口
interface IA<T, R> {
//下面的语句将会报错
//T n = 10;
//因为在接口中,属性都默认为 public static final 修饰
//在普通方法中,可以使用泛型
default void f1(T t) {
}
}
//在继承接口时,指定泛型的类型
interface IB extends IA<String, Integer> {
}
//在实现接口时,指定泛型的类型
class Dog implements IA<Double, String> {}
//如果没有指定,则泛型类型默认为 Object
(3)自定义泛型方法
基本语法:
修饰符 <T, R, ...> 返回类型 方法名(参数列表) {
}
注意事项:
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会确定
public void eat(E e) {}
,修饰符后没有 <T, R, …> eat 方法不是泛型方法,而是使用了泛型。
//在普通类中定义泛型方法
class A {
//泛型方法
public <T, R> void f1(T t, R r) {}
}
//在泛型类中定义泛型方法
class B<T, R> {
public <E> void m1() {}
}
public class Test {
public static void main(String[] arts) {
//调用泛型方法时,会确定泛型的类型
A a = new A();
//泛型方法的,T , R 会根据我们传入的类型自动确定
//下面的语句,T = Integer; R = String
//int 会自动进行装箱转为 Integer
a.f1(10, "xiong");
}
}
4 泛型的继承和通配符
-
泛型不具备继承性
List<Object> list = new ArrayList<String>();
这条语句是错误的,虽然String 是 Object 的子类, 但是泛型不具备继承性。 - <?> :支持任意泛型类型。
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
//<?> :支持任意泛型类型
test1(list1);
test1(list2);
public void test1(List<?> c) {
for (Object object : c) { // 通配符,取出时,就是Object
System.out.println(object);
}
}
- <? extends A> :支持 A 类以及 A 类的子类,规定了泛型的上限。
? 表示我们要传入的泛型类型,它可以是 A 类,以及继承了 A 类的子类,不限于直接子类
- <? super A> :支持 A 类以及 A 类的父类,不限于直接父类,规定了泛型的下限。
? 表示我们要传入的泛型类型,它可以是 A 类,以及 A 类的父类,不限于直接父类
原文地址:https://blog.csdn.net/weixin_74200123/article/details/145311786
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!