2017-06-06 17 views
1

ジェネリックをプロトコルと一緒に使用することについて、Web上で次の例が見つかりましたが、ジェネリックスが必要な理由はわかりません。プロトコルを使用するだけです。ここにジェネリックが必要なのはなぜですか?プロトコルは十分ではありませんか?

我々はプロトコルを定義します。

protocol Healthy { 
    mutating func setAlive(status: Bool) 
    var health: Int { get } 
} 

そしてジェネリックadotpingを使用して機能のプロトコル

func check<T:Healthy>(inout object: T) { 
    if (object.health <= 0) { 
     object.setAlive(false) 
    } 
} 

ことを私は以下のようにコードを変更しましたし、すべてが今でも結構です。

func check(object: inout Healthy) { 
    if (object.health <= 0) { 
     object.setAlive(status: false) 
    } 
} 

そうではありませんか?

プロトコルに関連するタイプがあり、それをインスタンスとして使用できない場合は、そこでジェネリックを使用すると思う唯一の理由があります。

答えて

1

さまざまなことを表現しています。

func check(object: inout Healthy) { 

object引数はどのHealthy準拠したインスタンスをすることができます。したがって、あなたがこれを行うことができます:

protocol Healthy {} 

struct Foo : Healthy {} 
struct Bar : Healthy {} 

func check(object: inout Healthy) { 
    object = Bar() 
} 

var h: Healthy = Foo() 
check(object: &h) 
print(h) // Bar() 

を我々はcheck(object:)と呼ばれ、hを通過した(Fooインスタンスを保持する)inout引数として、しかしBarインスタンスを保持hになってしまいました。

具体的にはcheck(object:)を具体的な型のinout引数で呼び出すことはできません。以下はコンパイルされない:Foo変数に割り当てないobject引数に任意Healthy適合するインスタンスを割り当てることができ

var h = Foo() 

// compiler error: Cannot pass immutable value as inout argument: 
// implicit conversion from 'Foo' to 'Healthy' requires a temporary 
check(object: &h) 

check(object:)ので。

func check<T : Healthy>(object: inout T) { 

object引数としかしながら

は、Healthyに準拠単一特定具象型である(このタイプは、コールサイトで満たされています)。 inout引数として渡される変数型と互換性がない可能性があるため、任意のHealthyに準拠するインスタンスを割り当てることはできません。

これで、具体的なタイプのinout引数で呼び出すことができるようになりました。私たちは今、言うことができます:

protocol Healthy { 
    var alive: Bool { get set } 
} 

struct Foo : Healthy { 
    var alive: Bool 
} 
struct Bar : Healthy { 
    var alive: Bool 
} 

func check<T : Healthy>(object: inout T) { 

    object.alive = false 

    // illegal 
    // object = Bar() 
} 

var h = Foo(alive: true) 
check(object: &h) 

(ノートhFooとして入力することが可能である)

だから、ほとんどの場合、あなたはおそらく、むしろプロトコルに型指定されたinoutを持つよりも、この方法は一般的なようにしたいでしょうパラメータは、具体的な型を扱う可能性が高いためです。

+0

あなたの答えから、私は、この例でジェネリックを使用する主な理由は、関数内での代入を避けることであると推測しています(この場合、inoutパラメータを指定すると概念的に間違っています)。正しい? – aneuryzm

+0

@パトリックジェネリックを使う主な理由は、 'inout'引数が単一の具体的な型であることを強制することです(具体的な型の変数を' inout Healthy'パラメータに渡すことはできません)。関数内の代入を避けることはできますが、引き続き引数に代入することはできますが、コンパイラは指定する型が 'T'型であり、*任意の*' Healthy'適合型ではないことを強制します。 – Hamish

+0

@Patrickたとえば、 'func check (オブジェクト:inout T、other:T){object = other}'は合法です。 'inout'引数に割り当てる能力は概念的に間違っているわけではありません - それは' inout'を使うことが可能な機能です。 – Hamish

0

これはinout(ugh)を使用しているため、値を返さないため、ジェネリックは不要です。私は...メソッドのシグネチャは、このようなものだった場合は、この例のための一般的である使用する場合は

func check<T:Healthy>(object: T) -> T { 
} 

これは、渡されたオブジェクトの種類と返される型が同じタイプであることを確実にするでしょう。ジェネリックなし

あなたは

struct A: Healthy {} 

...のインスタンスを渡すと

struct B: Healthy {} 

...のインスタンスを返すことができますうーん...多分これはまだの場合でありますinout。プロトコルに準拠した別の構造体を作成し、objectを新しい構造体のインスタンスに変更できますか?たぶん...後でそれをチェックしなければならないでしょう。

+0

私は試してみましたが、正しく宣言している限り、どちらの場合でも変数を割り当てることができます(ジェネリックの場合は具象型、それ以外の場合はプロトコル)。だから、拘束を持たない限り(ジェネリックオブジェクトと同じものを返すなど)、ジェネリックを使う理由はわかりません。 – aneuryzm

関連する問題