Java中的泛型
Java中的泛型
泛型是一种参数化类型的机制。它可以使得代码适用于各种类型,从而编写更加通用的代码,例如集合框架。
泛型是一种编译时类型确认机制。它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException(类型转换错误异常)
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数
使用泛型与不使用泛型的集合的区别
不使用泛型的集合
优点:集合不使用泛型,默认是Object类型,可以存储任何类型的元素
缺点:不安全,会引发异常
import java.util.ArrayList;
public class leeCode {
public static void main(String[] args) {
ArrayList e = new ArrayList();
// 可以往集合里添加任意类型的元素
e.add("字符串");
e.add(1);
// 因为无法确定集合里的对象类型,所以使用每个对象独有的方法
// 会抛出异常
for (Object k : e) {
String s = (String)k;
System.out.println(s.length());
}
}
}
使用泛型的集合
优点:避免了类型转换的麻烦,存取元素类型一致
缺点:指定了什么类型,就只能是什么类型,无法存入其他元素
import java.util.ArrayList;
public class leeCode {
public static void main(String[] args) {
// 使用泛型指定了集合存储的类型为String
ArrayList<String> e = new ArrayList<String>();
e.add("字符串1");
// e.add(1); error:只能存储字符串
e.add("字符串2");
for (String k : e) {
System.out.println(k.length());
}
}
}
泛型的定义跟使用
作用:简单来说就是使一个类或者方法具有兼容性,可以根据我们需要的数据类型对某一种数据类型进行操作
定义和使用带有泛型的类
格式:
修饰符 class 类名<泛型变量>
Ex:
/*
API中的ArrayList集合
*/
// E 代表一个未知的数据类型
class ArrayList<E> {
public boolean add(E e) {
// 方法体
}
public E get(int index) {
// 方法体
}
}
- 什么时候确定泛型?
创建对象的时候确认泛型的类型
定义和使用带有泛型的方法
格式:
修饰符 <泛型变量> 返回值类型 方法名称 (参数)
Ex:
public class GenericMethodTest
{
// 泛型方法 printArray
// 一个方法实现对多种数据类型的数据输出的功能,具备了一定的兼容性
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "整型数组元素为:" );
printArray( intArray ); // 传递一个整型数组
System.out.println( "\n双精度型数组元素为:" );
printArray( doubleArray ); // 传递一个双精度型数组
System.out.println( "\n字符型数组元素为:" );
printArray( charArray ); // 传递一个字符型数组
}
}
- 什么时候确定泛型?
调用方法的时候确认泛型的类型
定义和使用带有泛型的接口
因为(继承)实现接口的时候对于泛型有两种情况,所以单独拿出来讨论。
当接口定义了泛型的时候,可以
1.实现(继承)时,保留接口(父类)的,这样相当于实现类(子类)也使用了泛型,在创建对象的时候确定泛型的数据类型。
2.实现(继承)时,直接把接口(父类)的写成想要实现的数据类型。
1、接口使用泛型
public interface MyInterFace<E> {
public abstract E func1();
public abstract E func2();
public abstract E func3();
}
2、实现时指定
public class MyImple implements MyInterFace<String> {
public String func1(){
System.out.println("fun1");
}
public String func2(){
System.out.println("fun2");
}
public String func3(){
System.out.println("fun3");
}
}
或者实现类依旧带泛型
public class MyImple<E> implements MyInterFace<E> {
public E func1(){
System.out.println("fun1");
return null;
}
public E func2(){
System.out.println("fun2");
return null;
}
public E func3(){
System.out.println("fun3");
return null;
}
}
泛型通配符
类型通配符一般是使用 ?代替具体的类型参数。例如 List 在逻辑上是List<String>,List<Integer> 等所有List<具体类型实参>的父类。
import java.util.*;
public class GenericTest {
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);
}
// 因为getData()方法的参数是List类型的,所以name,age,number都可以作为这个方法的实参,这就是通配符的作用
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
- 类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
import java.util.*;
public class GenericTest {
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);
// 1报错,因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number,所以泛型为String是不在这个范围之内,所以会报错
//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));
}
}
- 3、类型通配符下限通过形如 List来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例。
默默努力也能成为一个不输别人的人