Java中的泛型

本文深入解析Java泛型的概念,包括其工作原理、类型擦除、限定通配符、泛型方法与类的编写技巧,以及泛型在实际编程中的应用案例,如LRU缓存的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Java中的泛型是什么 ? 使用泛型的好处是什么?

 

  泛型是在编译期间能发现类型的错误,防止非定义的类型出现。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

 

好处:1、泛型可以使代码重复利用 2、泛型类型安全,提供编译时检查 3、性能不错

 

2. Java的泛型是如何工作的 ? 什么是类型擦除 ?

 

  泛型通过类型擦除进行工作的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List<String>在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。

 

类型擦除: 所有类型参数都用他们的限定类型替换:比如 T->Object 和 extends BaseClass->BaseClass

 

List<String> l1 = new ArrayList<String>(); List<Integer> l2 = new ArrayList<Integer>(); System.out.println(l1.getClass() == l2.getClass()); //打印的结果为 true 是因为 List<String> 和 List<Integer> 在 jvm 中的 Class 都是 List.class。 //在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 <T> 则会被转译成普通的 Object 类型, //如果指定了上限如 <T extends String> 则类型参数就被替换成类型上限。

 

1、类型检查:在生成字节码之前提供类型检查 2、类型擦除:所有类型参数都用他们的限定类型替换,包括类、变量和方法(类型擦除) 3、如果类型擦除和多态性发生了冲突时,则在子类中生成桥方法解决(擦除类型参数后吗,就不是重写了,破坏了类的多态性,方法名相同,但是不知道调用哪个) public void setXXX(Object second) //这里就调用了 重写的 父类 (Object) {setXXX((AAA)XXX)} //A表示自定的类型用父类还是自身 4、如果调用泛型方法的返回类型被擦除,则在调用该方法时插入强制类型转换

 

 

 

 

3. 什么是泛型中的限定通配符和非限定通配符 ?

 

<?> 被称作无限定的通配符。 //只能调用类中与类型无关的方法,什么add啊 remove啊都不能用了 <? extends T> 被称作有上限的通配符。 //只能由T和T的子类 <? super T> 被称作有下限的通配符。 //只能由T和T的父类

 

 

4. List<? extends T>和List <? super T>之间有什么区别 ?

 

List<? extends T>可以接受任何继承自T的类型的List,而List<? super T>可以接受任何T的父类构成的List。

 

5. 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?

 

但出于规范的目的,Java 还是建议我们用单个大写字母来代表类型参数。常见的如:  1. T 代表一般的任何类。  2. E 代表 Element 的意思,或者 Exception 异常的意思。  3. K 代表 Key 的意思。  4. V 代表 Value 的意思,通常与 K 一起配合使用。  5. S 代表 Subtype 的意思,文章后面部分会讲解示意。

public V put(K key, V value) { V v = value; return v; }

   

6. Java中如何使用泛型编写带有参数的类?

 

public class MultiType <E,T>{    E value1;    T value2;    public E getValue1(){        return value1;    }    public T getValue2(){        return value2;    } }

 

//泛型类与泛型方法的共存现象 public class Test1<T>{    public  void testMethod(T t){        System.out.println(t.getClass().getName());    }    public  <T> T testMethod1(T t){        return t;    } } Test1<String> t = new Test1(); t.testMethod("generic"); Integer i = t.testMethod1(new Integer(1));

 

7. 编写一段泛型程序来实现LRU缓存?(最近最少使用)

 

public class LRUCache2<K, V> extends LinkedHashMap<K, V> { private final int MAX_CACHE_SIZE; public LRUCache2(int cacheSize) { super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); MAX_CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Map.Entry<K, V> entry : entrySet()) { sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue())); } return sb.toString(); } }

 

8. 你可以把List<String>传递给一个接受List<Object>参数的方法吗?

 

这样做的话会导致编译错误。因为List<Object>可以存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储Strings。 

 

 

9. Array中可以用泛型吗?

 

Array事实上并不支持泛型,这也是为什么Joshua Bloch在Effective Java一书中建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。

 

10. 如何阻止Java中的类型未检查的警告?

 

如果你把泛型和原始类型混合起来使用,Java 5的javac编译器会产生类型未检查的警告,例如:List<String> rawList = new ArrayList();

 

 

11.泛型的缺点

List<String> a = new ArrayList()<>; a.add("11"); a.add(11); //正常情况下,因为泛型的限制,编译器不让最后一行代码编译通过,因为类似不匹配,但是,基于对类型擦除的了解,利用反射,我们可以绕过这个限制。 public interface List<E> extends Collection<E>{     boolean add(E e); } 上面是 List 和其中的 add() 方法的源码定义。 因为 E 代表任意的类型,所以类型擦除时,add 方法其实等同于 boolean add(Object obj);

 

 

12.泛型类或者泛型方法中,不接受 8 种基本数据类型

 

13.Java 不能创建具体类型的泛型数组

 

 

1/不能用基本类型实例化类型参数。不接受 8 种基本数据类型int/double/..。只接受Integer/Double/...之类 2/运行时类型查询只适用于原始类型 不要用if (a instanceof Pair<String>)而是(a.getClassO == b.getClass()) 3/不能创建参数化类型的数组 Pair<String>[] table = new Pair<String>[10]; // Error 4/Varargs警告 :public static <T> void addAll(Collections coll, T... ts)产生数组违反上述3,可以使用 @SafeVarargs 标注来消除创建泛型数组的有关限制 5/不能实例化类型变置:public Pair() { first = new T(); second = new T(); } // Error。 最好的解决办法是让调用者提供一个构造器表达式:Pair<String> p = Pair.makePairCString::new); public static <T> Pair<T> makePair(Supplier<T> constr) {return new Pair<>(constr.get0. constr.get0);} 6/不能构造泛型数组:public static <T extends Comparable〉T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // Error 类型擦除会让这个方法永远构造 Comparable[2] 数组 在这种情况下,最好让用户提供一个数组构造器表达式String[] ss = ArrayAlg.minmax (String[]::new,"Tom", "Dick", "Harry") minmax 方法使用这个参数生成一个有正确类型的数组: public static <T extends Comparable〉T[] minmax(IntFunction<TD> constr, T... a){T[] mm = constr.apply(2);} 7/泛型类的静态上下文中类型变量无效,不能在静态域或方法中引用类型 8/不能抛出或捕获泛型类的实例,不过在异常规范中使用类型变量是允许的 9/可以消除对受查异常的检查 10/当泛型类型被擦除时, 无法创建引发冲突的条件,记得获取桥方法 11/S是T的子类,那么Pair<S> T> 的一个子类吗? 答案是“ 不是”,泛型没有继承,

 

<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); } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值