2017-03-11 4 views
6

Ich bin derzeit eine DSL für eine Bibliothek zu schreiben, und ich mag Typen Metadaten verdinglichten Typen Parameter wie folgt liefern:Wie kann ich verdinglichte Daten in Instanzfeldern in Kotlin speichern?

val config = Config.create() 
      .consumerFor<MyType>{ 
       // consume 
      } 

Mein Problem ist, dass ich nur verwenden, kann das reified Schlüsselwort in inline Funktionen und in einer inline Funktion kann ich nicht Instanzfelder wie folgt verwenden:

inline fun <reified T> consumerFor(consumer: (T) -> Unit) { 
     consumers.put(T::class.java, consumer) 
     return this 
    } 

, weil ich einen Fehler:

Public-API inline function cannot access non-public-API 'private final val consumers...

Es scheint so weit zu sein, dass ich die Parameter des verdinglichten Typs nicht verwenden kann, wo sie am nützlichsten wären. Gibt es einen Workaround dafür?

Antwort

6

öffentliche inline Funktionen können private Erklärungen nicht direkt verwenden, da dann, wenn an den Aufrufstellen außerhalb der Klasse inlined, werden die Verbräuche falsche Zugriffsebene haben (auf JVM, ein private Mitglied einer Klasse kann nicht von außen zugegriffen werden kann).

Was Sie tun können, ist the internal visibility in Kotlin: auf JVM, die Mitglieder mit dieser Sichtbarkeit Modifikator werden in öffentlichen Mitgliedern mit ihren Namen gemangelt werden (daher immer noch sichtbar, aber nicht einfach von Java), und die Der Kotlin-Compiler wird zumindest die Verwendung des Kotlin-Codes kontrollieren.

Es gibt ein paar Möglichkeiten, um ein internal Mitglied zuzugreifen innerhalb eines public inline fun finden Sie diese Frage: (link)

In Ihrem speziellen Fall, würde ich es mit @PublishedApi lieber tun:

private val consumers = mutableMapOf<Class<*>, Any>() 

@PublishedApi 
internal fun <T> putConsumer(clazz: Class<out T>, consumer: (T) -> Unit) { 
    consumers.put(clazz, consumer) 
} 

inline fun <reified T> consumerFor(noinline consumer: (T) -> Unit): C { 
    putConsumer(T::class.java, consumer) 
    return this 
} 

Oder Wenn es Ihnen nichts ausmacht, consumers mit @PublishedApi zu exponieren, dann können Sie es wie folgt tun:

@PublishedApi 
internal val consumers = mutableMapOf<Class<*>, Any>() 

inline fun <reified T> consumerFor(noinline consumer: (T) -> Unit): C { 
    consumers.put(T::class.java, consumer) 
    return this 
} 
+0

Das wird gut, danke. Dies ist nur eine Konfigurationsklasse mit endgültigen Feldern, also macht es mir nichts aus. –

1

Wenn Sie nur den Parameter reified type benötigen, um seine Java-Klasse widerzuspiegeln, können Sie mit einer zusätzlichen Inline-Überladung mit zusätzlichen Parametern Class<T> arbeiten.

inline fun <reified T> consumerFor(noinline consumer: (T) -> Unit) = 
    consumerFor(T::class.java, consumer) 

fun <T> consumerFor(key: Class<T>, consumer: (T) -> Unit) = apply { 
    consumers.put(key, consumer) 
} 

Auf diese Weise können consumers Eigenschaft nicht effektiv veröffentlicht aussetzen haben. Der Bonuspunkt ist, dass Sie diese nichtlineare Überladung auch von Java aus aufrufen können.

Verwandte Themen