インタビューで、遺伝なしに多型を達成できるかどうか尋ねられました。これは可能ですか?
私が今まで読んだことのあるテーマに関する最も良い説明は、有名な型理論家 Luca Cardelli による記事です。記事の名前は 型、データの抽象化、多態性について です。
Cardelliは、この記事でいくつかのタイプの多態性を定義しています。
継承に関連する多型の種類は、包含多型またはサブタイプ多型に分類されます。
ウィキペディア は良い定義を提供します:
オブジェクト指向プログラミングでは、サブタイプポリモーフィズムまたは包含ポリモーフィズムは、タイプ理論における概念であり、名前がいくつかの共通のスーパークラスによって関連付けられている限り、名前は多くの異なるクラスのインスタンスを表す場合があります。包含ポリモーフィズムは、通常、サブタイプ化によってサポートされます。つまり、さまざまなタイプのオブジェクトは、別のタイプ(その基本タイプ)のオブジェクトに完全に置き換え可能であり、共通のインターフェースを介して処理できます。あるいは、包接多型は、型キャストとしても知られている型強制によって実現できます。
オブジェクト指向プログラミングにおけるポリモーフィズム と呼ばれる別のウィキペディアの記事もあなたの質問に答えているようです。
Javaのこのサブタイピング機能は、クラスやインターフェースの継承を通じて、他の手段の中でも特に実現されています。ただし、Javaのサブタイプ機能は、継承の観点から常に明らかであるとは限りません。ジェネリックとの共分散および反分散の例を見てみましょう。また、配列は直列化可能および複製可能ですが、型階層のどこにも明らかではありません。また、プリミティブ拡張変換により、Javaの数値演算子は多態的であり、特定のケースでは完全に関連のないオペランド(つまり、文字列と数値の連結、または文字列とその他のオブジェクトの連結)さえ受け入れます。プリミティブのボックス化とボックス化解除の場合も考慮してください。後者のポリモーフィズム(強制とオーバーロード)のケースは、継承とはまったく関係ありません。
インクルージョン
List<Integer> myInts = new ArrayList<Integer>();
これは、あなたの質問が参照しているように見えるケースです。つまり、ArrayListがListを実装する場合のように、タイプ間に継承または実装の関係がある場合です。
ただし、先ほど触れたように、Javaジェネリックを導入すると、サブタイプの規則が曖昧になることがあります。
List<? super Number> myObjs = new ArrayList<Object>();
List<? extends Number> myNumbers = new LinkedList<Integer>();
そして、他の場合では、関係はAPIでさえ明らかではありません
Cloneable clone = new int[10];
Serializable obj = new Object[10]
それでも、Cardelliによると、これらはすべて普遍的な多型の形式です。
パラメトリック
public <T> List<T> filter(Predicate<T> predicate, List<T> source) {
List<T> result = new ArrayList<>();
for(T item : source) {
if(predicate.evaluate(item)){
result.add(item);
}
return result;
}
}
同じアルゴリズムを使用して、すべての種類の述語ですべての種類のリストをフィルタリングできます。可能なすべての種類のリストに対して1行のコードを繰り返す必要はありません。実際のリストのタイプと述語のタイプはパラメトリックです。 JDK 8プレビュー で利用可能なラムダ式を使用したこの例を参照してください(述語実装の簡潔さについて)。
filter(x -> x % 2 == 0, asList(1,2,3,4,5,6)); //filters even integers
filter(x -> x % 2 != 0, asList(1L,2L,3L,4L,5L,6L)); //filters odd longs
filter(x -> x >= 0.0, asList(-1.0, 1.0)); //filters positive doubles
Cardelliによれば、これは普遍的な多型の形式です。
強制
double sum = 1 + 2.0;
整数演算と浮動小数点演算は完全に異なります。ここで、タイプの異なる2つのオペランドにプラス演算子を適用することは、なんらかの形式の強制なしでは不可能です。
この例では、integer型とdouble型は、明示的なキャストを行わずに自動的にdouble型に強制変換(変換)されます。表現全体が2倍に昇格されます。これは、Javaにプリミティブな拡大変換があるためです。
Cardelliによれば、この形式の自動強制は、プラス演算子に提供されるアドホックな多態性の形式です。
明示的なキャストなしでは整数と浮動小数点数を合計することさえできない言語があります(つまり、AFAIK、SML、ところで、パラメトリックな多態性がこの種の問題を克服するための鍵となります)。
オーバーロード
double sum = 2.0 + 3.0;
String text = "The sum is" + sum;
ここでのプラス演算子は、使用する引数に応じて2つの異なることを意味します。明らかに、オペレーターは過負荷になっています。これは、オペランドのタイプによって実装が異なることを意味します。 Cardelliによれば、これはプラス演算子に提供されるアドホックなポリモーフィズムの形式です。
もちろん、これはクラスのメソッドオーバーロードの形式にも適用されます(つまり、Java.lang.Mathメソッドのminとmaxは、異なるプリミティブ型をサポートするためにオーバーロードされます)。
これらのポリモーフィズムのいくつかの実装で継承が重要な役割を果たす場合でも、それが唯一の方法ではありません。オブジェクト指向ではない他の言語は、他の形態のポリモーフィズムを提供します。たとえば、Pythonのような動的言語またはGoなどの静的型付け言語の ダックタイピング の場合、またはSML、Ocaml、Scalaなどの言語の 代数的データ型 の場合を考えてみましょう。または タイプクラス Haskellなどの言語、 マルチメソッド Clojure、 プロトタイプ継承 JavaScriptなど.
アドホックな多態性>演算子のオーバーロード>継承なし
アドホックな多態性>メソッドのオーバーロード>継承なし
アドホックな多態性>メソッドのオーバーライド>継承あり
パラメトリック多態性>ジェネリックス>継承なし
サブタイプポリモーフィズムまたは包含ポリモーフィズム>ポリモーフィック代入>継承あり
サブタイプポリモーフィズムまたは包含ポリモーフィズム>ポリモーフィック戻り型>継承あり
サブタイプ多態性または包含多態性>多態性引数タイプ>継承あり
強制多態性>拡張>継承ありまたはなし
強制多態性>自動ボックス化とボックス化解除>継承なし
強制多態性> Var args>継承なし
強制多態性>型キャスト>継承なし
承知しました。 Javaでは、2つのクラスに同じインターフェースを実装させることができ、その結果は多態的です。機能は継承されません。
_public interface Foo {
public int a();
}
public class A implements Foo {
public int a() {
return 5;
}
}
public class B implements Foo {
public int a() {
return 6;
}
}
_
次に、他の場所:
_Foo x = new A();
System.out.println(x.a())
Foo y = new B();
System.out.println(y.a())
_
x
とy
はどちらもFoo
sですが、a()
を呼び出したときの結果は異なります。
関数のオーバーロードは、継承なしで実現できる多態性の1つです(実際の多態性とは異なります)。
例えば.
class Foo {
public void Arrest( Animal A){
/*code...*/
}
public void Arrest( Terrorist T ) {
/*code...*/
}
}
from main :
Foo f= new Foo();
f.Arrest( new Lion() );
f.Arrest(new Terrorist());
Arrestメソッドは2回呼び出されますが、コードの実行パスは異なります。
*これも真の多型ではありません。一般の真の多型は、継承なしでは達成できません。
はい、おそらくインターフェイスによるポリモーフィズムについて聞きたかったのでしょう。したがって、同じインターフェースから実装する2つのクラスがある場合、そのようなインターフェースを持つオブジェクトを検査するすべての場所で使用できます。ウィキペディアのコードを参照してください:
// from file Animal.Java
public interface Animal {
public String talk();
}
// from file Cat.Java
public class Cat implements Animal {
@Override
public String talk() {
return "Cat says Meow!";
}
}
// from file Dog.Java
public class Dog implements Animal {
@Override
public String talk() {
return "Dog says Woof! Woof!";
}
}
// from file PolymorphismExample.Java
public class PolymorphismExample {
public static void main(String[] args) {
Collection<Animal> animals = new ArrayList<Animal>();
animals.add(new Cat());
animals.add(new Dog());
for (Animal a : animals) {
System.out.println(a.talk());
}
}
}
静的タイプ
オーバーロード-同じ名前で異なるシグネチャを持つ複数のメソッドを意味し、オーバーライドなしで可能です
class StaticPolyExample
{
void print(int s)
{
//print s
}
void print(String s)
{
//print s
}
}
動的タイプ
オーバーライド-継承が必要なサブクラスでスーパークラスのメソッドが再定義されることを意味します
class Printer
{
void print(String s)
{
// prints String
}
}
class diffPrinter extends Printer
{
void print(String s)
{
// prints String differently
}
}