ホームページ Java &#&チュートリアル Javaのワイルドカードの詳細な分析(コード)

Javaのワイルドカードの詳細な分析(コード)

Sep 08, 2018 pm 04:46 PM
ワイルドカード

この記事では、Java のワイルドカードの詳細な分析 (コード) を紹介します。必要な方は参考にしてください。

ジェネリック型のサブタイプの無関係性については、この記事の前の部分で説明しました。しかし、場合によっては、ジェネリック型を通常の型と同じように使用できるようにしたいことがあります。

◆ ジェネリック オブジェクトへの参照のアップキャスト

◆ ジェネリック オブジェクトへの参照のダウンキャスト

ジェネリック オブジェクトのアップキャスト Quote

たとえば、次のようにします。たくさんの箱があり、各箱には異なる果物が入っており、果物の入った箱を普遍的に処理する方法を見つける必要があるとします。より一般的には、A は B のサブタイプであり、型 C のインスタンスを型 C の宣言に割り当てる方法を見つける必要があります。

これを実現するには、次の例のように、ワイルドカードを含む拡張宣言を使用する必要があります:

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
ログイン後にコピー

"? extends" はジェネリック型のサブタイプです。 依存関係が現実になります: Apple は Fruit Type のサブタイプです。 List は List

汎用オブジェクトへの参照をダウンキャストします

次に、別のワイルドカードを紹介します: ? super。タイプ B がタイプ A のスーパータイプ (親タイプ) である場合、C は C< スーパー A> のサブタイプになります:

List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits;
ログイン後にコピー
ログイン後にコピー

ワイルドカード マーカーの使用はなぜ機能しますか?

原理はもう明らかです: この新しい文法構造をどのように活用するか?

? extends

Java 配列のサブタイプの依存関係について説明する、この第 2 部で使用した例をもう一度見てみましょう:

Apple[] apples = new Apple[ 1 ];
Fruit[] fruits = apples;
fruits[ 0 ] = new Strawberry();
ログイン後にコピー
ログイン後にコピー

ご覧のとおり、Strawberry オブジェクトを追加した後、Fruit として宣言されたオブジェクトに移動すると、 Apple オブジェクト配列の配列の場合、コードはコンパイルできますが、実行時に例外がスローされます。

これで、ワイルドカードを使用して関連コードをジェネリックに変換できます。Apple は Fruit のサブクラスであるため、List オブジェクトの定義を extends Fruit> に割り当てることができます。 ; の発言:

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
fruits.add( new Strawberry());
ログイン後にコピー

今回はコードをコンパイルできません! Java コンパイラにより、フルーツ リストにイチゴを追加できなくなります。コンパイル時にエラーを検出できるため、実行時に互換性のない型がリストに追加されていないかどうかをチェックする必要はありません。 Fruit オブジェクトをリストに追加しても、それは機能しません:

fruits.add( new Fruit());
ログイン後にコピー

これはできません。実際、extends を使用するデータ構造に値を書き込むことはできません。

理由は非常に簡単で、次のように考えることができます: この ? extends T ワイルドカードは、型 T のサブタイプを扱っていることをコンパイラーに伝えますが、このサブタイプが何であるかはわかりません。確認する方法がないため、型の安全性を確保するために、この型のデータを追加することは許可されていません。一方、それがどのような型であっても、それは常に型 T のサブタイプであることがわかっているため、データを読み取るときに、取得したデータが型 T のインスタンスであることを確認できます。 super

super ワイルドカードを使用する場合の一般的な状況は何ですか?まずこれを見てみましょう:

Fruit get = fruits.get( 0 );
ログイン後にコピー

fruits が Apple のスーパータイプの一部を保持する List を指していることがわかります。繰り返しますが、スーパークラスが何であるかは正確にはわかりませんが、Apple と Apple のサブクラスがその型と互換性があることはわかっています。この未知の型は Apple であり、GreenApple のスーパークラスであるため、次のように書くことができます:

List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits;
ログイン後にコピー
ログイン後にコピー

Apple のスーパークラスを追加したい場合、コンパイラーは次のように警告します:

fruits.add( new Apple());
fruits.add( new GreenApple());
ログイン後にコピー

なぜなら、私たちはそれを知らないからです。そのようなインスタンスはすべて参加できません。

このタイプからデータを取得するにはどうすればよいですか?オブジェクト インスタンスを取り出すことしかできないことがわかりました。スーパークラスが何であるかわからないため、コンパイラが保証できるのはそれがオブジェクトであることだけです。オブジェクトは任意の Java 型のスーパークラスであるためです。

アクセス原則と PECS ルール

? extends と ? スーパー ワイルドカードの特徴をまとめると、次の結論を導き出すことができます:

◆ データ型からデータを取得したい場合は、? extends ワイルドカードを使用します。

◆ オブジェクトをデータ構造に書き込む場合は、? スーパー ワイルドカードを使用します

◆ 保存と取得の両方を行う場合は、ワイルドカードを使用しないでください。

これは、Maurice Naftalin が著書『Java Generics and Collections』でアクセス原則と呼んでいるものであり、Joshua Bloch が著書『Effective Java』で PECS ルールと呼んでいるものです。

ブロック氏は、PECS は「Producer Extends, Consumer Super」の略であり、覚えやすく使いやすいことを思い出させました。

上記は下から続きます:

The Java Tutorial

java Generics and Collections、Maurice Naftalin および Philip Wadler 著

Effective Java Chinese Edition (2nd Edition)、Joshua Bloch 著

尽管有这么多丰富的资料,有时我感觉,有很多的程序员仍然不太明白Java泛型的功用和意义。这就是为什么我想使用一种最简单的形式来总结一下程序员需要知道的关于Java泛型的最基本的知识。

Java泛型由来的动机

理解Java泛型最简单的方法是把它看成一种便捷语法,能节省你某些Java类型转换(casting)上的操作:

List<Apple> box = ...;
Apple apple = box.get( 0 );
ログイン後にコピー

上面的代码自身已表达的很清楚:box是一个装有Apple对象的List。get方法返回一个Apple对象实例,这个过程不需要进行类型转换。没有泛型,上面的代码需要写成这样:

List box = ...;
Apple apple = (Apple) box.get( 0 );
ログイン後にコピー

很明显,泛型的主要好处就是让编译器保留参数的类型信息,执行类型检查,执行类型转换操作:编译器保证了这些类型转换的绝对无误。

相对于依赖程序员来记住对象类型、执行类型转换——这会导致程序运行时的失败,很难调试和解决,而编译器能够帮助程序员在编译时强制进行大量的类型检查,发现其中的错误。

泛型的构成

由泛型的构成引出了一个类型变量的概念。根据Java语言规范,类型变量是一种没有限制的标志符,产生于以下几种情况:

◆ 泛型类声明

◆ 泛型接口声明

◆ 泛型方法声明

◆ 泛型构造器(constructor)声明

泛型类和接口

如果一个类或接口上有一个或多个类型变量,那它就是泛型。类型变量由尖括号界定,放在类或接口名的后面:

public interface List<T> extends Collection<T> {
...
}
ログイン後にコピー

简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。

Java类库里的很多类,例如整个Collection框架都做了泛型化的修改。例如,我们在上面的第一段代码里用到的List接口就是一个泛型类。在那段代码里,box是一个List对象,它是一个带有一个Apple类型变量的List接口的类实现的实例。编译器使用这个类型变量参数在get方法被调用、返回一个Apple对象时自动对其进行类型转换。

实际上,这新出现的泛型标记,或者说这个List接口里的get方法是这样的:

 T get( int index);
ログイン後にコピー

get方法实际返回的是一个类型为T的对象,T是在List声明中的类型变量。

泛型方法和构造器(Constructor)

非常的相似,如果方法和构造器上声明了一个或多个类型变量,它们也可以泛型化。

 public static <t> T getFirst(List<T> list)
ログイン後にコピー

这个方法将会接受一个List类型的参数,返回一个T类型的对象。

例子

你既可以使用Java类库里提供的泛型类,也可以使用自己的泛型类。

类型安全的写入数据…

下面的这段代码是个例子,我们创建了一个List实例,然后装入一些数据:

List<String> str = new ArrayList<String>();
str.add( "Hello " );
str.add( "World." );
ログイン後にコピー

如果我们试图在List装入另外一种对象,编译器就会提示错误:

 str.add( 1 ); // 不能编译
ログイン後にコピー

类型安全的读取数据…

当我们在使用List对象时,它总能保证我们得到的是一个String对象:

 String myString = str.get( 0 );
ログイン後にコピー

遍历

类库中的很多类,诸如Iterator,功能都有所增强,被泛型化。List接口里的iterator()方法现在返回的是Iterator,由它的T next()方法返回的对象不需要再进行类型转换,你直接得到正确的类型。

for (Iterator<String> iter = str.iterator(); iter.hasNext();) {
String s = iter.next();
System.out.print(s);
}
ログイン後にコピー

使用foreach

“for each”语法同样受益于泛型。前面的代码可以写出这样:

for (String s: str) {
System.out.print(s);
}
ログイン後にコピー

这样既容易阅读也容易维护。

自动封装(Autoboxing)和自动拆封(Autounboxing)

在使用Java泛型时,autoboxing/autounboxing这两个特征会被自动的用到,就像下面的这段代码:

List<Integer> ints = new ArrayList<Integer>();
ints.add( 0 );
ints.add( 1 );
int sum = 0 ;
for ( int i : ints) {
sum += i;
}
ログイン後にコピー

然而,你要明白的一点是,封装和解封会带来性能上的损失,所有,通用要谨慎的使用。

子类型

在Java中,跟其它具有面向对象类型的语言一样,类型的层级可以被设计成这样:

Javaのワイルドカードの詳細な分析(コード)

在Java中,类型T的子类型既可以是类型T的一个扩展,也可以是类型T的一个直接或非直接实现(如果T是一个接口的话)。因为“成为某类型的子类型”是一个具有传递性质的关系,如果类型A是B的一个子类型,B是C的子类型,那么A也是C的子类型。在上面的图中:

◆ FujiApple(富士苹果)是Apple的子类型

◆ Apple是Fruit(水果)的子类型

◆ FujiApple(富士苹果)是Fruit(水果)的子类型

所有Java类型都是Object类型的子类型。

B类型的任何一个子类型A都可以被赋给一个类型B的声明:

Apple a = ...;
Fruit f = a;
ログイン後にコピー

泛型类型的子类型

如果一个Apple对象的实例可以被赋给一个Fruit对象的声明,就像上面看到的,那么,List 和 a List之间又是个什么关系呢?更通用些,如果类型A是类型B的子类型,那C 和 C之间是什么关系?

答案会出乎你的意料:没有任何关系。用更通俗的话,泛型类型跟其是否子类型没有任何关系。

这意味着下面的这段代码是无效的:

下面的同样也不允许:

为什么?一个苹果是一个水果,为什么一箱苹果不能是一箱水果?

在某些事情上,这种说法可以成立,但在类型(类)封装的状态和操作上不成立。如果把一箱苹果当成一箱水果会发生什么情况?

如果可以这样的话,我们就可以在list里装入各种不同的水果子类型,这是绝对不允许的。

另外一种方式会让你有更直观的理解:一箱水果不是一箱苹果,因为它有可能是一箱另外一种水果,比如草莓(子类型)。

这是一个需要注意的问题吗?

应该不是个大问题。而程序员对此感到意外的最大原因是数组和泛型类型上用法的不一致。对于泛型类型,它们和类型的子类型之间是没什么关系的。而对于数组,它们和子类型是相关的:如果类型A是类型B的子类型,那么A[]是B[]的子类型:

可是稍等一下!如果我们把前面的那个议论中暴露出的问题放在这里,我们仍然能够在一个apple类型的数组中加入strawberrie(草莓)对象:

这样写真的可以编译,但是在运行时抛出ArrayStoreException异常。因为数组的这特点,在存储数据的操作上,Java运行时需要检查类型的兼容性。这种检查,很显然,会带来一定的性能问题,你需要明白这一点。

重申一下,泛型使用起来更安全,能“纠正”Java数组中这种类型上的缺陷。

现在估计你会感到很奇怪,为什么在数组上会有这种类型和子类型的关系,我来给你一个《Java Generics and Collections》这本书上给出的答案:如果它们不相关,你就没有办法把一个未知类型的对象数组传入一个方法里(不经过每次都封装成 Object[]),就像下面的:

泛型出现后,数组的这个个性已经不再有使用上的必要了(下面一部分我们会谈到这个),实际上是应该避免使用

相关推荐:

Java中的访问修饰符详细解析

详解Java Reference源码分析代码

以上がJavaのワイルドカードの詳細な分析(コード)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

オラクルのワイルドカードとは何ですか? オラクルのワイルドカードとは何ですか? Nov 08, 2023 am 10:02 AM

Oracle のワイルドカード文字には、「%」、「_」、「[]」、および「[^]」が含まれます。詳細な紹介: 1. ワイルドカード文字「%」は、ゼロ文字を含む任意の文字と一致することを意味します。Oracle でワイルドカード文字「%」を使用すると、あいまいクエリを実装できます。クエリ ステートメントでワイルドカード文字「%」を使用すると、クエリは指定された文字に一致するすべての文字を返します。パターン マッチング文字列; 2. ワイルドカード文字「_」は、任意の 1 文字と一致することを意味します。Oracle では、ワイルドカード文字「_」を使用して完全一致を実現できます。クエリでワイルドカード文字を使用する場合発言など

正規表現のワイルドカードとは何ですか? 正規表現のワイルドカードとは何ですか? Nov 17, 2023 pm 01:40 PM

正規表現のワイルドカードには、「.」、「*」、「+」、「?」、「^」、「$」、「[]」、「[^]」、「[a-z]」、「[A-Z]」が含まれます。 、"[0-9]"、"\d"、"\D"、"\w"、"\W"、"\s&quo

ファイルを検索するときのワイルドカード文字 * と ? は何を意味しますか? ファイルを検索するときのワイルドカード文字 * と ? は何を意味しますか? Jul 25, 2022 am 11:43 AM

ワイルドカード文字「*」は任意の文字列を表し、ゼロ文字、単一文字、または複数の文字と一致しますが、ワイルドカード文字「?」は単一の文字列のみを表すため、この単語は存在する必要があります。ワイルドカードは、ファイルのあいまい検索に使用される、主にアスタリスク「*」と疑問符「?」を含む特別なステートメントです。「*」の一致数には制限はありませんが、「?」の一致文字数には制限があります。フォルダを検索するとき、これを使用して 1 つ以上の実際の文字を置き換えることができます。実際の文字がわからない場合、またはフルネームを入力するのが面倒な場合は、ワイルドカードを使用して 1 つ以上の実際の文字を置き換えることがよくあります。

ワイルドカード文字 * と ? の違いは何ですか? ワイルドカード文字 * と ? の違いは何ですか? Aug 11, 2023 pm 01:20 PM

ワイルドカード * と ? には、使用法と一致範囲にいくつかの違いがあります。具体的な違い: 1. 一致範囲に関して、ワイルドカード * は文字、数字、句読点、スペースなどを含む任意の長さの文字シーケンスと一致しますが、ワイルドカード ? は 1 文字のみと一致します。使用法では、ワイルドカード * が使用されます ファジー マッチングでは、複数の文字または文字シーケンスと一致します。ワイルドカード文字 ? は完全一致に使用され、1 文字のみと一致します。

Wordのワイルドカード文字とは何ですか? Wordのワイルドカード文字とは何ですか? Jan 22, 2024 pm 04:03 PM

Word のワイルドカード文字には、「?」、「*」、「[]」、「!」、「%」などが含まれます。詳細な紹介: 1. 疑問符 (?): 任意の 1 文字と一致することを示します。たとえば、「c?t」は「cat」や「cut」などの単語と一致します。2. アスタリスク (*): 0 個以上の文字と一致することを示します。たとえば、「colr」は「color」や「colour」などの単語と一致します。3. 角括弧 ([]): 角括弧内の任意の文字と一致することを意味します。たとえば、「[aeiou]」は任意の母音、4. 感嘆符などに一致します。

ゼロから学ぶ: Linux コマンドのワイルドカードをマスターする ゼロから学ぶ: Linux コマンドのワイルドカードをマスターする Mar 16, 2024 pm 12:18 PM

ゼロから学ぶ: Linux コマンドのワイルドカード文字をマスターする Linux システムでは、ワイルドカード文字はファイル名と一致するために使用される特殊文字です。これらのワイルドカードをマスターすると、コマンド ラインでファイルをより効率的に見つけて操作できるようになります。この記事では、初心者がこれらのワイルドカード文字をよりよく理解して使用できるように、一般的に使用されるいくつかの Linux ワイルドカード文字に焦点を当て、具体的なコード例を示します。 1. アスタリスク (*) アスタリスク ワイルドカードは 0 個以上の任意の文字を表し、任意の長い文字列と一致します。以下に例を示します。ls

Java 関数の汎用ワイルドカード: 上限と下限 Java 関数の汎用ワイルドカード: 上限と下限 Apr 25, 2024 pm 04:18 PM

Java では、ジェネリック ワイルドカードを使用すると、具体的な型を指定せずにジェネリック型を表すことができます。上限ワイルドカード (

マスターすべき一般的な CSS セレクター ワイルドカードの例 マスターすべき一般的な CSS セレクター ワイルドカードの例 Dec 26, 2023 am 09:00 AM

一般的な CSS セレクター ワイルドカードの例を習得するには、特定のコード サンプルが必要です。CSS セレクターは、Web 開発の非常に重要な部分です。これにより、さまざまな要素属性に基づいて HTML 要素を選択し、スタイルを設定できます。 CSS セレクターの中でも、ワイルドカードは、あらゆる種類の HTML 要素に一致する非常に便利なセレクターです。この記事では、一般的に使用される CSS ワイルドカードを紹介し、具体的なコード例を示します。ワイルドカード (*) ワイルドカード「*」は、すべての HTML 要素の選択を表します。すべての設定に使用できます

See all articles