web-dev-qa-db-ja.com

ジェネリックスはSwift

私のアプリケーションでは、指定されたタイプTに依存するオブジェクトの配列を作成するジェネリックメソッドを作成したいと思います。

次の関数を作成しました。

func getArray<T : ROJSONObject>(key:String) -> T[] {
    var elements = T[]()

    for jsonValue in getValue(key).array! {
        var element = T()

        element.jsonData = jsonValue
        elements.append(element)
    }

    return elements
}

ここで、メソッドを呼び出すときに型を渡したいので、内部でどの型を作成する必要があるかがわかります。 JavaとC#では、次のような方法を使用できると思います。

object.getArray<Document>("key")

私がそれをそのように呼ぶとき、私はいつもエラーを受け取ります:

Cannot explicitly specialize a generic function

したがって、私の修正は、タイプTのインスタンスを含む追加のパラメーターを定義して、タイプを自動的に検出することでした。

func getArray<T : ROJSONObject>(key:String, type:T) -> T[] {
    var elements = T[]()

    for jsonValue in getValue(key).array! {
        var element = T()

        element.jsonData = jsonValue
        elements.append(element)
    }

    return elements
}

未使用のインスタンスを渡さずにその動作を取得する他の方法は本当にありませんか?それとも私は何かを誤解していますか?

さらなるテスト

Jtbandesの回答の後、私はさらにいくつかのテストを行いました。呼び出しにasを追加して、Typeを強制しようとしました。

class Person {

    init() { }

    func getWorkingHours() -> Float {
        return 40.0
    }
}

class Boss : Person {
    override func getWorkingHours() -> Float {
        println(100.0)
        return 100.0
    }
}

class Worker : Person {
    override func getWorkingHours() -> Float {
        println(42.0)
        return 42.0
    }
}

func getWorkingHours<T : Person>() -> T {
    var person = T()
    person.getWorkingHours()

    return person
}

var worker:Worker = getWorkingHours() as Worker
var boss:Boss = getWorkingHours() as Boss
worker.getWorkingHours() // prints out 40.0 instead of 42.0
boss.getWorkingHours() // prints out 40.0 instead of 100.0

したがって、asキーワードで型を指定したとしても、どういうわけか、型は常に基本型です。この例はあまり意味がないことは知っていますが、それはテスト目的のためだけのものでした。

16
Prine

バグだと思います。

クラスをNSObjectのサブクラスにするか、基本クラスのコンストラクターを@requiredでマークすることで、この問題を回避できます。

import Cocoa

class A : NSObject {
    init() { }
}
class B : A {}
class C : A {}

func Create<T:NSObject> () -> T {
    return T()
}

println(Create() as A)
println(Create() as B)
println(Create() as C)

//<_TtC11lldb_expr_01A: 0x7f85ab717bc0>
//<_TtC11lldb_expr_01B: 0x7f85ab451e00>
//<_TtC11lldb_expr_01C: 0x7f85ab509160>

class D {
    @required init() { } 
}

class E : D {
    init() { }
}

class F : D {
    init() { }
}

func Create2<T:D> () -> T {
    return T()
}

println(Create2() as D)
println(Create2() as E)
println(Create2() as F)

//C11lldb_expr_01D (has 0 children)
//C11lldb_expr_01E (has 1 child)
//C11lldb_expr_01F (has 1 child)

@requiredが問題を解決する理由がわかりません。しかし これは参照です

必須

この属性をクラスの指定された初期化子または便利な初期化子に適用して、すべてのサブクラスがその初期化子を実装する必要があることを示します。

必要な指定イニシャライザーは明示的に実装する必要があります。必要なコンビニエンスイニシャライザーは、明示的に実装することも、サブクラスがスーパークラスの指定されたイニシャライザーをすべて直接実装する場合(またはサブクラスが指定されたイニシャライザーをコンビニエンスイニシャライザーでオーバーライドする場合)に継承することもできます。

4
Bryan Chen

Swiftランタイム関数unsafeBitCastから借用して、これを回避しました。

func unsafeBitCast<T, U>(x: T, _: U.Type) -> Uとして宣言されており、unsafeBitCast(value, MyType)と呼ぶことができます。

あなたの関数に適用すると、これは

func getArray<T : ROJSONObject>(key:String, _: T.Type) -> T[] {
    // function stays the same
}

そして、あなたはそれをこのように呼ぶことができます

getArray("key", Document)
15
Alfonso

asキーワードでタイプを示すことができるはずです:object.getArray("key") as Document[]

6
jtbandes

初期化子をrequiredにしてから、_let realType = T.self_行を追加し、T()realType()に置き換える必要があります。

_class Person {
    required init() { }

    func getWorkingHours() -> Float {
        return 40.0
    }
}

class Boss : Person {
    override func getWorkingHours() -> Float {
        println(100.0)
        return 100.0
    }
}

class Worker : Person {
    override func getWorkingHours() -> Float {
        println(42.0)
        return 42.0
    }
}

func getWorkingHours<T : Person>() -> T {
    let realType = T.self
    var person = realType()
    person.getWorkingHours()

    return person
}
_
2
mixel

質問のコードの作業バージョン(第2部)

•関数は一般的な人物タイプを返すため、初期化が必要です(newPerson(as person:T) -> T

•クラスメソッドfunc getWorkingHours()を_var workingHours { get }_に置き換えました。より「迅速」な方法で同等に機能します。

getWorkingHours()は、名前が示すように、Personではなく時間を返します。

_class Person {
    required init() {}
    var workingHours: Float { get { return 40 } }
}

class Boss : Person {
    override var workingHours: Float { get { return 100 } }
}

class Worker : Person {
    override var workingHours: Float { get { return 42 } }
}

func getWorkingHours<T : Person>(_ person: T) -> Float {
    return person.workingHours
}

func newPerson<T : Person>(as person: T) -> T {
    return T()
}

// -----------------------------------------------

let worker = Worker()
let boss = Boss()

print( getWorkingHours(worker) ) // prints 42.0
print( getWorkingHours(boss) ) // prints 100.0

let person = newPersonOfSameClass(as: worker)

print( getWorkingHours(person) ) // prints 42.0
_

お役に立てば幸いです。このコードは、遊び場でコピー/貼り付けする準備ができています。

0
Moose

のように呼ばないでください

object.getArray<Document>("key")

代わりに、次のように「<Document>」の部分を省略します。

object.getArray("key")

私の問題を修正しました。

0
Lukas Kalinski

あなたの質問の最初の部分に答えるために

戻り値の型として[T]をすでにバットしているので、Tの型を指定するためにメソッドに追加のパラメーターを追加する必要はありません。

したがって、この行を置き換えます

object.getArray<Document>("key")

これとともに

object.getArray("key") as [Document]

このようにして、タイプをTに指定する機会が得られます。

0
thesummersign