1.ジェネリックとは何ですか?
Javaの一般的な設計原則:コンパイル時に警告がない限り、実行時にClassCastException例外は発生しません。
ジェネリックス:オブジェクトが作成されるか、メソッドが呼び出されて特殊な型を指定するまで、型固有の作業を延期します
パラメータ化されたタイプ:
-
タイプをパラメータとして渡す
-
<数据类型>
参照型のみにすることができます
関連用語:
-
ArrayList<E>
型パラメータ変数と呼ばれている中E -
ArrayList<Integer>
整数は、実際の型引数を呼び -
ArrayList<E>
ジェネリック型と呼ばれる全体 -
全体は
ArrayList<Integer>
パラメータ化されたタイプと呼ばれますParameterizedType
2.なぜジェネリックが必要なのですか
初期のJavaでは、Objectは任意の型を表すために使用されていましたが、下向きの変換には強制変換の問題があったため、プログラムは安全ではありませんでした。
まず、それについて考えてみましょう。ジェネリックがなければ、コレクションはどうなるでしょうか。
-
コレクションとマップコレクションには、要素のタイプに関する制限はありません。元々、私のコレクションにはすべてのDogオブジェクトがロードされていましたが、Catオブジェクトをコレクションの外部に保存する際に文法上のエラーはありませんでした。
-
オブジェクトをコレクションにスローします。コレクションは要素のタイプが何であるかを認識していませんが、それがオブジェクトであることを認識しているだけです。したがって、get()を実行すると、Objectが返されます。外部のオブジェクトを取得しますが、変換を強制する必要もあります
ジェネリック医薬品の場合:
-
コードはより簡潔です[強制変換なし]
-
プログラムはより堅牢です[コンパイル中に警告がない限り、実行時にClassCastException例外は発生しません]
-
読みやすさと安定性[コレクションを作成する場合、タイプは制限されます]
2.1ジェネリックスを取得したら、拡張を使用してコレクションをトラバースします
コレクションを作成するときに、コレクションのタイプを明確にしたので、拡張されたforを使用してコレクションをトラバースできます。
//创建集合对象
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
//遍历,由于明确了类型.我们可以增强for
for (String s : list) {
System.out.println(s);
}
第三に、ジェネリック医薬品の基礎
3.1ジェネリッククラス
ジェネリッククラスは、クラスのジェネリック型を定義することです。ユーザーがクラスを使用すると、型が明確になります...このようにして、ユーザーはクラスが表す型を明確にします...ユーザーはそれを使用する必要はありません心配実行時の強制変換と異常変換について。
-
クラスで定義されたジェネリック型は、クラスのメソッドでも使用できます。
/*
1:把泛型定义在类上
2:类型变量定义在类上,方法中也可以使用
*/
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
-
テストコード:
ユーザーが使用するタイプは、作成時にタイプを指定します。使用すると、クラスはユーザーが使用したいタイプに自動的に変換されます。
public static void main(String[] args) {
//创建对象并指定元素类型
ObjectTool<String> tool = new ObjectTool<>();
tool.setObj(new String("钟福成"));
String s = tool.getObj();
System.out.println(s);
//创建对象并指定元素类型
ObjectTool<Integer> objectTool = new ObjectTool<>();
/**
* 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.
*/
objectTool.setObj(10);
int i = objectTool.getObj();
System.out.println(i);
}
3.2一般的な方法
ジェネリッククラスは以前に導入されました。クラスで定義されたジェネリック型は、メソッドでも使用できます。
さて、特定のメソッドでジェネリックを使用するだけでよいかもしれません...外の世界はメソッドだけを気にし、クラスの他のプロパティは気にしません...この場合、クラス全体でジェネリックを定義します。少し大騒ぎ。
-
ジェネリックメソッドを定義する...ジェネリックを最初に定義してから使用する
//定义泛型方法..
public <T> void show(T t) {
System.out.println(t);
}
-
テストコード:
ユーザーから渡されるタイプと、戻り値は何ですか?
public static void main(String[] args) {
//创建对象
ObjectTool tool = new ObjectTool();
//调用方法,传入的参数是什么类型,返回值就是什么类型
tool.show("hello");
tool.show(12);
tool.show(12.5);
}
3.3ジェネリッククラスから派生したサブクラス
ジェネリッククラスは以前に定義しました。ジェネリッククラスはジェネリックの特性を備えたクラスであり、本質的にJavaクラスであるため、継承できます。
それはどのように受け継がれますか??ここには2つのケースがあります
-
サブクラスは、汎用クラスの型パラメーター変数を指定します
-
サブクラスがあいまいで、汎用クラスの型パラメーター変数
3.3.1サブクラスは、汎用クラスの型パラメーター変数を指定します
-
汎用インターフェース
/*
把泛型定义在接口上
*/
public interface Inter<T> {
public abstract void show(T t);
}
-
ジェネリックインターフェイスを実装するクラス.....
/**
* 子类明确泛型类的类型参数变量:
*/
public class InterImpl implements Inter<String> {
@Override
public void show(String s) {
System.out.println(s);
}
}
3.3.2サブクラスは、ジェネリッククラスの型パラメーター変数について明確ではありません
-
サブクラスがジェネリッククラスの型パラメーター変数について明確でない場合、サブクラスが外部で使用される場合、型パラメーター変数を渡す必要があり、型パラメーター変数を実装クラスで定義する必要があります。
/**
* 子类不明确泛型类的类型参数变量:
* 实现类也要定义出<T>类型的
*
*/
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
テストコード:
public static void main(String[] args) {
//测试第一种情况
//Inter<String> i = new InterImpl();
//i.show("hello");
//第二种情况测试
Inter<String> ii = new InterImpl<>();
ii.show("100");
}
注目に値するのは:
-
クラスの実装が親クラスのメソッドをオーバーライドする場合、戻り値の型は親クラスの型と同じである必要があります。
-
クラスで宣言されたジェネリックは、非静的メンバーに対してのみ有効です。
3.4ワイルドカードを入力
なぜタイプワイルドカードが必要なのですか????需要を見てみましょう。
ここで要件があります。メソッドはコレクションパラメーターを受け取り、コレクションをトラバースしてコレクション要素を出力します。どうすればよいですか?
-
以前にジェネリックについて学んだことがなかったことによると、これを行う可能性があります。
public void test(List list){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
上記のコードは正しいですが、コンパイル時にコレクション要素のタイプが決定されていないという警告が表示されます...これはエレガントではありません...
-
それで、ジェネリックについて学びました。今、何をすべきでしょうか??一部の人々はこれを行うかもしれません:
public void test(List<Object> list){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
これを行うための構文に問題はありませんが、ここで注意する価値があります。test ()メソッドは、Objectがロードされたコレクションのみをトラバースできます。!!
強調する:ジェネリック医薬品がされている<Object>
以前のように継承されていない、と言うことですつまりList<Object>
、彼らはList<String>
とは何の関係もありません!!!!
私は今どうすればいい???Listコレクションに読み込まれる要素のタイプがわからないList<Objcet>
ため、これは機能しません...では、Javaジェネリックはタイプワイルドカードを提供しますか?
したがって、コードは次のように変更する必要があります。
public void test(List<?> list){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
?ワイルドカードは、任意のタイプを照合でき、任意のJavaクラスを照合できることを意味します....。
ここで、?ワイルドカードを使用する場合、オブジェクトのタイプに関連しないメソッドのみを呼び出すことができ、オブジェクトのタイプに関連するメソッドを呼び出すことはできないことに注意してください。
オブジェクトとは関係のないメソッドのみを呼び出すことができ、オブジェクトのタイプに関連するメソッドを呼び出すことはできないことを忘れないでください。外の世界がそれを使用するまで、特定のタイプが何であるかがわからないからです。つまり、上記のListコレクションでは、add()メソッドを使用できません。add()メソッドがオブジェクトをコレクションにスローし、オブジェクトのタイプがわからなくなったためです。
3.4.1ワイルドカードの上限を設定する
まず、ワイルドカードの上限を設定する場所を見てみましょう。
ここで、数値要素[Float、Integer、Double、Byteなどの数値タイプはOK]でのみ操作できるListコレクションを受け取りたいのですが、どうすればよいですか???
ワイルドカードを学習しましたが、ワイルドカードを直接使用すると、セットは数値だけを操作することはできません。したがって、ワイルドカードの上限を設定する必要があります
List<? extends Number>
上記のコードの意味:Listコレクションにロードされる要素は、Numberまたはそれ自体のサブクラスのみになります。
public static void main(String[] args) {
//List集合装载的是Integer,可以调用该方法
List<Integer> integer = new ArrayList<>();
test(integer);
//List集合装载的是String,在编译时期就报错了
List<String> strings = new ArrayList<>();
test(strings);
}
public static void test(List<? extends Number> list) {
}
3.4.2ワイルドカードの下限を設定する
ワイルドカードの上限を設定する方法を説明したので、ワイルドカードの下限を設定することはなじみのないことではありません。文法を見てみましょう
//传递进来的只能是Type或Type的父类
<? super Type>
ワイルドカードの下限を設定することは珍しくありません。TreeSetコレクションには...があります。見てみましょう。
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
それの用途は何ですか??TreeSet<String>
型の変数を作成し、Stringのサイズを比較できるComparatorを渡す場合は、それについて考えてみましょう。
次に、このコンパレータには多くの選択肢がComparator<String>
ありますComparator<Objcet>
。たとえば、タイプパラメータが文字列である親クラスにすることもできます。
そうすることで、それは非常に柔軟です。つまり、文字列のサイズを比較できる限り、それは可能です。
ワイルドカードの上限を設定するか下限を設定するかは、オブジェクトに関連するメソッドを操作できないことに注意してください。ワイルドカードが含まれている限り、そのタイプは不明です。
3.5ワイルドカードと一般的な方法
ほとんどの場合、ワイルドカードの代わりに汎用メソッドを使用できます....。
//使用通配符
public static void test(List<?> list) {
}
//使用泛型方法
public <T> void test2(List<T> t) {
}
上記の両方の方法が可能です...では、問題は、ワイルドカードまたは汎用メソッドのどちらを使用する必要があるかということです。?
原則として:
-
パラメータ間の依存関係のタイプ、またはパラメータの値に戻る場合は、との間の依存関係です。次に、一般的な方法を使用します
-
依存関係がない場合は、より柔軟なワイルドカードを使用します。
3.6一般的な消去
ジェネリックスは、使用するjavacコンパイラーに提供され、コレクションの入力タイプを制限するために使用されます。これにより、コンパイラーはソースコードレベルで、つまり、コレクションへの不正なデータの挿入をブロックします。ただし、コンパイラがジェネリックを使用してJavaプログラムをコンパイルすると、生成されたクラスファイルにはジェネリック情報が含まれなくなるため、プログラムの効率に影響はありません。このプロセスは「消去」と呼ばれます。
3.6.1互換性
JDK5はジェネリックの概念を提案しましたが、JDK5には以前はジェネリックがありませんでした。つまり、ジェネリックはJDK5以下のコレクションと互換性がある必要があります。
ジェネリック特性を持つコレクションを古いバージョンのコレクションに割り当てると、ジェネリックは消去されます。
注目に値するのは、typeパラメーターの上限を予約することです。
List<String> list = new ArrayList<>();
//类型被擦除了,保留的是类型的上限,String的上限就是Object
List list1 = list;
タイプパラメータのないコレクションをタイプパラメータのあるコレクションに割り当てるとどうなりますか??
List list = new ArrayList();
List<String> list2 = list;
エラーは報告されず、「未チェックの変換」というプロンプトが表示されるだけです。
第四に、ジェネリックの適用
Webページを作成する場合、複数のDAOが存在することが多く、毎回複数のDAOを作成する必要があるため、少し面倒です。
では、私たちが望む効果は何ですか??抽象DAOを作成するだけで、他のDAOは、抽象DAOを継承する限り、対応するメソッドを持ちます。
この効果を実現するには、ジェネリックを使用する必要があります。抽象DAOでは、どのDAOがそれ自体を継承するかを知ることができないため、特定のタイプは不明です。ジェネリック型は、作成時に特定の型を指定することです。
-
抽象DAO
public abstract class BaseDao<T> {
//模拟hibernate....
private Session session;
private Class clazz;
//哪个子类调的这个方法,得到的class就是子类处理的类型(非常重要)
public BaseDao(){
Class clazz = this.getClass(); //拿到的是子类
ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass(); //BaseDao<Category>
clazz = (Class) pt.getActualTypeArguments()[0];
System.out.println(clazz);
}
public void add(T t){
session.save(t);
}
public T find(String id){
return (T) session.get(clazz, id);
}
public void update(T t){
session.update(t);
}
public void delete(String id){
T t = (T) session.get(clazz, id);
session.delete(t);
}
}
-
抽象DAOを継承する実装クラスには、追加、削除、変更、およびチェックのための対応するメソッドがあります。
CategoryDao
public class CategoryDao extends BaseDao<Category> {
}
BookDao
public class BookDao extends BaseDao<Book> {
}