Java中的泛型

一、引入泛型程序的背景

在Java泛型类出现之前,程序员必须使用Object编写适用于多种类型的代码。这很繁琐,也很不安全。随着JDK5泛型的引入,编写代码可以对多种不同类型的对象重用。

二、泛型语法

泛型类(generic class)就是有一个或多个类型变量的类。

1.定义泛型类

下面这个泛型类引入了一个类型变量T(Type),用“<>”括起来,放在类名的后面,“<>”表示这里面的所有类型变量在泛型类被实例化时,需要初始化其类型。泛型类还可以有很多个类型变量,均写在“<>”里,用“,”隔开,如ClassName<T, U>。类型变量在整个类定义中用于指定方法的返回类型以及字段和局部变量的类型,如下面的field就是一个该类的T数据类型的属性字段。

public class ClassName<T>{
	private T field;
}

2.定义泛型方法

泛型方法不仅仅只能在泛型类中定义,也可以在普通类中定义。现在将上面定义的泛型类进行扩充,添加构造方法和其他方法。

  • 当方法返回的是T类型的值时,类型变量“< T >”应放在修饰符“public static”后,返回类型“T”前。
public class ClassName<T>{
	private T field;
	
	public ClassName(T field){
		this.field = field;
	}
	
	public T method1(){
		//do same work
		return parameter;
	}
	
	public static <T> T method2(){
		//do same work
		return parameter;
	}
}
  • 在调用泛型方法时,可以把具体类型包裹在“<>”中,放在方法名前面。几乎在所有情况下,“<>”可以省略,因为编译器能够推断出T的类型。但是,当返回类型存在多种情况下,编译器将会报错。
String str = Class.<String>method2();	//未省略<String>
String str = Class.method2();			//省略<String>

3.类型变量的限定

当类型变量T的实例需要调用某一个方法时,必须限制T的类型,即将T类型继承某个类或接口,这样就能调用该类或接口里的方法。类型变量限定的语法如下,多个限定用“&”分隔。其中多个限定只能有一个是类,其它为接口,且类必须放在第一位。这与Java中类的继承类似,因为类只能继承一个超类,但却能实现多个接口。为保持类型安全性,必要时虚拟机会插入强制类型转换。

<T extends BoundingType1 & BoundingType2 & BoundingType3>

4.虚拟机如何运行泛型代码

虚拟机中没有泛型的概念,只有普通的类和方法。所以在执行代码时,会将所有类型参数替换为它们的限定类型(没有限定类型的将用Object替换)或叫原始类型(raw type),这被称作类型擦除。为避免替换类型变量后,继承类方法的重写出现问题,编译器会在继承的类中合成一个桥方法(bridge method),使该桥方法变为重写方法,原重写方法变为重载方法。

/**
*程序员写的代码
*/
public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}

class DateInterval extends Pair<LocalDate>{
    public void setSecond(LocalDate second){
        if(second.compareTo(getFirst())>=0){
            super.setSecond(second);
        }
    }
}
/**
*虚拟机将 T类型替换成限定类型(Object)后的代码
*/
public class Pair {
    private Object first;
    private Object second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(Object first, Object second) {
        this.first = first;
        this.second = second;
    }

    public Object getFirst() {
        return first;
    }

    public void setFirst(Object first) {
        this.first = first;
    }

    public Object getSecond() {
        return second;
    }

    public void setSecond(Object second) {
        this.second = second;
    }
}

class DateInterval extends Pair{

	//原重写的方法在这里变为重载方法
    public void setSecond(LocalDate second){
    	//getFirst()类型也会被虚拟机进行强制转换
        if(second.compareTo((LocalDate)getFirst())>=0){
            super.setSecond(second);
        }
    }
    
	//虚拟机合成的桥方法(此方法变为重写方法)
    public void setSecond(Object second){
        setSecond((LocalDate)second);
    }
}

三、限制与局限性

1.不能用基本数据类型实例化类型参数

Java的8种基本数据类型不能代替类型参数。因为基本数据类型不存在限定类型,因此虚拟机没法进行类型擦除,就无法运行该代码。

2.运行时类型查询只适用于原始类型

虚拟机在运行时,会擦除T类型变量,因此查询到的类均为原始类型,这些查询包括instanceof()、getClass()等方法。

3.不能创建参数化类型的数组

虚拟机在运行时,会擦除T类型变量,故只能创建普通类的数组。

Class1<Class2>[] class1;	//合法
new Class1<Class2>[10];		//不合法

4.不能实例化类型变量

虚拟机在运行时,会擦除T类型变量,故T会被视为其原始类型。

T parameter;	//合法
new T();		//不合法

5.不能构造泛型数组

同上,虚拟机在运行时,会擦除T类型变量,故T会被视为其原始类型。

T[] array;		//合法
new T()[10];	//不合法

6.泛型类的静态上下文中类型变量无效

不能在静态字段或方法中引用类型变量。因为在创建对象时,如果声明了两个不同T(如String和Integer)的该类时,该静态字段无法共享,因为它只能是具体的某个数据类型。静态方法也是如此。因此,禁止使用带有类型变量的静态字段和方法。

public class ClassName<T>{
	private static T field;			//不合法
	
	//合法
	public static <T> T method(){
		//do same work
		return parameter;
	}
	
	//不合法
	public static T method(){
		//do same work
		return parameter;
	}
}

7.不能抛出或捕获泛型类的实例

不要用泛型类继承Throwable,因为catch子句中不能使用类型变量。

<think>好的,我现在需要帮助用户了解Java的使用方法和示例。首先,用户提供的引用内容提到类、方法、静态方法的以及伪的概念。我需要结合这些信息,整理出一个结构清晰的回答,并确保遵循系统级指令中的格式要求。 首先,用户可能对的基本概念不太清楚,所以需要先简单解释什么是,以及它的作用,比如类安全、代码复用等。然后,分点介绍的不同用法,比如类、方法、接口,每个部分都要有示例代码,并且代码中的数学表达式要用$...$格式,比如在解释方法时,如果有涉及类参数T,可以用$T$来表示。 接下来,需要注意引用内容中提到的伪,说明Java在编译时类擦除的特性,这可能会影响运行时无法获取的具体类。此外,用户可能想知道如何使用通配符和边界,比如上界和下界通配符,这部分需要给出示例,并正确应用LaTeX格式,比如$? extends Number$。 用户还提到静态方法的,需要特别注意静态方法不能使用类定义的,而必须自己声明,这点要强调,避免常见错误。示例代码需要正确展示如何在静态方法中定义类参数。 最后,生成相关问题时要考虑用户可能进一步想知道的内容,比如类擦除的影响、通配符的使用场景、与集合框架的结合,以及类边界的作用。确保每个问题都有针对性,帮助用户深入理解。 现在检查是否有遗漏,比如是否覆盖了所有引用中的要点,引用的标识是否正确添,示例代码是否符合语法,数学表达式是否正确使用$符号。同时,确保回答结构清晰,分点明确,符合用户可见层的要求。</think>### Java使用方法及示例 Java用于实现**类参数化**,提高代码复用性和类安全性[^1]。以下是具体用法及示例: --- #### 一、类 定义类时声明类参数$T$,可在类中作为类使用: ```java public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } // 使用示例 Box<String> stringBox = new Box<>(); stringBox.setContent("Hello"); ``` --- #### 二、方法 在方法返回值前声明类参数$T$,可在非类中使用: ```java public class Utils { public static <T> T getMiddle(T[] arr) { return arr[arr.length/2]; } } // 调用示例 String[] names = {"Alice", "Bob", "Charlie"}; String middle = Utils.<String>getMiddle(names); // 返回"Bob" ``` --- #### 三、接口 定义接口时声明类参数,常用于集合类: ```java public interface List<T> { void add(T element); T get(int index); } // 实现示例 class StringList implements List<String> { // 必须使用String类实现方法 } ``` --- #### 四、类边界 限制范围: ```java public static <T extends Number> double sum(T a, T b) { return a.doubleValue() + b.doubleValue(); } // 只能传入Number及其子类 sum(10, 20.5); // 合法 sum("a", "b"); // 编译错误 ``` --- #### 五、通配符 处理未知类集合时使用: ```java // 上界通配符 void process(List<? extends Number> list) { /* 可读不可写 */ } // 下界通配符 void addNumbers(List<? super Integer> list) { /* 可写不可读 */ } ``` --- #### 关键特性 1. **类擦除**:编译后会被替换为Object或边界类[^3] 2. **静态方法限制**:静态方法需单独声明: ```java public static <T> void print(T item) { System.out.println(item); } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值