제네릭의 개념 (제네릭)은 Java SE5의 주요 변경 사항 중 하나입니다. 제네릭은 매개변수화된 유형(매개변수화된 유형) 개념을 구현하여 코드를 여러 유형에 적용할 수 있도록 합니다. "일반"이라는 용어는 "많은 유형에 적용 가능"을 의미합니다.
제네릭 메서드가 있는 클래스가 제네릭인지 여부와는 아무런 관련이 없습니다. 즉, 제네릭 메서드가 있는 클래스가 제네릭 클래스일 수도 있고 아닐 수도 있습니다.
일반 메소드를 사용하면 클래스와 독립적으로 메소드를 변경할 수 있습니다.
기본 원칙: 가능하다면 일반적인 방법을 사용해야 합니다. 즉, 일반 메서드를 사용하여 전체 클래스를 일반화하는 것을 대체할 수 있다면 상황이 더 명확해지므로 일반 메서드만 사용해야 합니다.
static
메서드의 경우 일반 클래스의 유형 매개변수에 액세스할 수 없습니다. 따라서 static
메서드가 일반 기능을 사용해야 하는 경우 Generic이어야 합니다. 행동 양식. static
方法而言,无法访问泛型类的类型参数,所以,如果static
方法需要使用泛型能力,就必须使其成为泛型方法。
要定义泛型方法,只需将泛型参数列表置于返回值之前。
使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断(type argument inference)。
类型推断只对赋值操作有效。
如果将一个泛型方法调用的结果作为参数,传递给另一个方法,这时编译器并不会执行类型推断。
在点操作符与方法名之间插入尖括号,然后把类型置于尖括号内,即显式的类型说明。
根据JDK文档的描述,
Class.getTypeParameters()
将“返回一个TypeVariable
对象数组,表示有泛型声明的类型参数…..”,这好像是在暗示你可能发现参数类型的信息,但是,正如你从输出中看到,你能够发现的只是用作参数占位符的标识符,这并非有用的信息。因此,残酷的现实是:在泛型代码内部,无法获得任何有关泛型参数类型的信息。
因此,你可以知道诸如泛型参数标识符和泛型类型边界这类信息——你却无法知道创建某个特定实例的实际的类型参数。……,在使用Java泛型工作时它是必须处理的最基本的问题。
Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此
Listf7e83be87db5cd2d9a8a0b8117b38cd4
和Listc0f559cc8d56b43654fcbe4aa9df7b4a
在运行时事实上是相同的类型。这两种形式都被擦除成它们的“原生类型,即List
。
일반 메소드를 사용할 때 일반적으로 매개변수 유형을 지정할 필요는 없습니다. 컴파일러가 특정 유형을 찾아주기 때문입니다. 이를 형식 인수 추론이라고 합니다.它怎么知道
f()
方法是为类型参数T而存在的呢?当你实例化这个模板时,C++编译器将进行检查,因此在Manipulatorda98a08537ace02ebe5efb4a0b237f4a
被实例化的这一刻,它看到HasF
拥有一个方法f()
일반 메서드를 정의하려면 반환 값 앞에 일반 매개변수 목록을 넣으면 됩니다. 1.1 유형 매개변수 추론
유형 추론은 할당 작업에만 유효합니다.
일반 메소드 호출의 결과가 매개변수로 다른 메소드에 전달되면 컴파일러는 유형 추론을 수행하지 않습니다.
2 삭제의 미스터리 🎜🎜🎜JDK 문서에 따르면 🎜1.1.1 명시적 유형 지정
도트 연산자와 메소드 이름 사이에 꺾쇠괄호를 삽입한 다음 꺾쇠괄호 안에 유형을 배치합니다. 즉, Explicit 유형 설명 .Class.getTypeParameters()
🎜는 "There를 나타내는 🎜TypeVariable
🎜 객체 배열을 반환합니다. 일반 선언된 유형 매개변수는...'입니다. 이는 매개변수 유형에 대한 정보를 찾을 수 있음을 암시하는 것처럼 보이지만 출력에서 볼 수 있듯이 찾을 수 있는 것은 🎜🎜매개변수 자리표시자🎜🎜로 사용되는 것뿐입니다. 식별자 🎜🎜는 유용한 정보가 아닙니다. 🎜🎜가혹한 현실은 다음과 같습니다. 🎜🎜일반 코드 내에서는 일반 매개변수 유형에 대한 정보를 얻을 수 있는 방법이 없습니다🎜🎜. 🎜🎜따라서 🎜일반 매개변수 식별자🎜 및 🎜일반 유형 경계🎜와 같은 것을 알 수 있지만 🎜특정 인스턴스를 생성하는 실제 유형 매개변수🎜는 알 수 없습니다. ..., 이는 Java Generics로 작업할 때 🎜다뤄야 하는 🎜가장 기본적인 🎜문제🎜입니다. 🎜🎜Java 제네릭은 🎜🎜erasure🎜🎜를 사용하여 구현됩니다. 즉, 제네릭을 사용할 때 모든 특정 유형 정보가 지워집니다. 🎜알 수 있는 유일한 것은 객체를 사용하고 있다는 것🎜입니다. 따라서 🎜List<string></string>
🎜과 🎜List<integer>🎜는 실제로 런타임 시 🎜🎜동일한 유형🎜🎜입니다. 두 양식 모두 🎜<code>List
🎜인 "🎜🎜네이티브 유형🎜🎜으로 지워집니다. 🎜🎜🎜2.1 C++ 방식 🎜🎜 2.1.1 다음 C++ 템플릿 예: 🎜🎜 🎜방법 이 템플릿을 🎜인스턴스화🎜하면 C++ 컴파일러가 이를 확인하므로Manipulatorfbe8c61f1a02b7601fcfb06421ff21e7
,set()
方法不能工作于Apple
和Fruit
,因为 set() 的参数也是? extends Furit
,这意味着它可以是任何事物,而编译器无法验证“任何事物”的类型安全性。但是,
equals()
方法工作良好,因为它将接受Object类型而并非T类型的参数。因此,编译器只关注传递进来和要返回的对象类型,它并不会分析代码,以查看是否执行了任何实际的写入和读取操作。5.2 逆变(Contravariance)
使用超类型通配符。声明通配符是由某个特定类的任何基类界定的,方法是指定
bbee182344df36d3890f89a83f7ca198
,甚至或者使用类型参数:117c5a0bdb71ea9a9d0c2b99b03abe3e
。这使得你可以安全地传递一个类型对象到泛型类型中。参数apples是Apple的某种基类型的List,这样你就知道向其中添加Apple或Apple的子类型是安全的。
package net.mrliuli.generics.wildcards;import java.util.*;public class SuperTypeWildcards { /** * 超类型通配符使得可以向泛型容器写入。超类型边界放松了在可以向方法传递的参数上所作的限制。 * @param apples 参数apples是Apple的某种基类型的List,这样你就知道向其中添加Apple或Apple的子类型是安全的。 */ static void writeTo(List<? super Apple> apples){ apples.add(new Apple()); apples.add(new Jonathan()); //apples.add(new Fruit()); // Error } }
GenericWriting.java 中
writeExact(fruitList, new Apple());
在JDK1.7中没有报错,说明进入泛型方法writeExact()
时T
被识别为Fruit
,书中说报错,可能JDK1.5将T
识别为Apple
。package net.mrliuli.generics.wildcards;import java.util.*;/** * Created by li.liu on 2017/12/8. */public class GenericWriting { static <T> void writeExact(List<T> list, T item){ list.add(item); } static List<Apple> appleList = new ArrayList<Apple>(); static List<Fruit> fruitList = new ArrayList<Fruit>(); static void f1(){ writeExact(appleList, new Apple()); writeExact(fruitList, new Apple()); } static <T> void writeWithWildcard(List<? super T> list, T item){ list.add(item); } static void f2(){ writeWithWildcard(appleList, new Apple()); writeWithWildcard(fruitList, new Apple()); } public static void main(String[] args){ f1(); f2(); } }5.3 无界通配符(Unbounded wildcards)
原生泛型
Holder
与Holder6b3d0130bba23ae47fe2b8e8cddf0195
原生
Holder
将持有任何类型的组合,而Holder6b3d0130bba23ae47fe2b8e8cddf0195
将持有具有某种具体类型的同构集合,因此不能只是向其中传递Object。5.4 捕获转换
以下示例,被称为捕获转换,因为未指定的通配符类型被捕获,并被转换为确切类型。参数类型在调用
f2()
的过程中被捕获,因此它可以在对f1()
的调用中被使用。package net.mrliuli.generics.wildcards;/** * Created by leon on 2017/12/9. */public class CaptureConversion { static <T> void f1(Holder<T> holder){ T t = holder.get(); System.out.println(t.getClass().getSimpleName()); } static void f2(Holder<?> holder){ f1(holder); // Call with captured type } public static void main(String[] args){ Holder raw = new Holder<Integer>(1); f1(raw); f2(raw); Holder rawBasic = new Holder(); rawBasic.set(new Object()); f2(rawBasic); Holder<?> wildcarded = new Holder<Double>(1.0); f2(wildcarded); } }6 问题
基本类型不能作为类型参数
由于探险,一个类不能实现同一个泛型接口的两种变体
由于擦除,通过泛型来重载方法将产生相同的签名,编译出错,不能实现重载
基类劫持了接口
7 总结
我相信被称为泛型的通用语言特性(并非必须是其在Java中的特定实现)的目的在于可表达性,而不仅仅是为了创建类型安全的容器。类型安全的容器是能够创建更通用代码这一能力所带来的副作用。
泛型正如其名称所暗示的:它是一种方法,通过它可以编写出更“泛化”的代码,这些代码对于它们能够作用的类型有更少的限制,因此单个的代码段能够应用到更多的类型上。
相关文章:
위 내용은 자바 프로그래밍 사고 학습 수업(3) 15장 - 제네릭의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!