Olgun Kotlin kodunun çoğunda, aşağıdaki modellerden birini bulacaksınız. Mülkiyet Delegelerini kullanan yaklaşım , Kotlin'in en küçük kodu üretme gücünden yararlanır.
Not: Buradaki kod içindir, java.util.Logging
ancak aynı teori herhangi bir günlük kütüphanesi için geçerlidir
Statik benzeri (ortak, sorudaki Java kodunuzun eşdeğeri)
Günlük sistemi içindeki bu karma aramanın performansına güvenemiyorsanız, bir örneği tutabilen ve sizin için statik gibi hissedebileceğiniz bir tamamlayıcı nesne kullanarak Java kodunuza benzer davranışlar elde edebilirsiniz.
class MyClass {
companion object {
val LOG = Logger.getLogger(MyClass::class.java.name)
}
fun foo() {
LOG.warning("Hello from MyClass")
}
}
çıktı oluşturma:
26 Aralık 2015 11:28:32 AM org.stackoverflow.kotlin.test.MyClass
foo BİLGİ: Merhaba MyClass
Buradaki tamamlayıcı nesneler hakkında daha fazla bilgi: Tamamlayıcı Nesneler ... Ayrıca yukarıdaki MyClass::class.java
örnekte Class<MyClass>
kaydedici için türün örneğini alırken this.javaClass
, türün örneğini alacağınızı unutmayın Class<MyClass.Companion>
.
Sınıf Örneği Başına (ortak)
Ancak, örnek düzeyinde bir günlükçü çağırmaktan ve almaktan kaçınmak için gerçekten hiçbir neden yoktur. Bahsettiğiniz deyimsel Java yolu modası geçmiş ve performans korkusuna dayanıyor, oysa sınıf başına kaydedici zaten gezegen üzerindeki neredeyse tüm makul kayıt sistemleri tarafından önbelleğe alınmış. Logger nesnesini tutmak için bir üye oluşturmanız yeterlidir.
class MyClass {
val LOG = Logger.getLogger(this.javaClass.name)
fun foo() {
LOG.warning("Hello from MyClass")
}
}
çıktı oluşturma:
26 Aralık 2015 11:28:44 AM org.stackoverflow.kotlin.test.MyClass foo BİLGİ: Merhaba MyClass
Hem örnek başına hem de sınıf varyasyonları başına performans testi yapabilir ve çoğu uygulama için gerçekçi bir fark olup olmadığını görebilirsiniz.
Mülkiyet Delegeleri (ortak, en zarif)
@Jire tarafından başka bir cevapta önerilen başka bir yaklaşım, daha sonra istediğiniz herhangi bir sınıfta mantığı eşit bir şekilde yapmak için kullanabileceğiniz bir özellik temsilcisi oluşturmaktır. Kotlin Lazy
zaten bir delege sağladığı için bunu yapmanın daha basit bir yolu var, sadece bir işlevde sarabiliriz. Buradaki bir hile, şu anda temsilci kullanan sınıfın türünü bilmek istiyorsak, onu herhangi bir sınıfta bir uzantı işlevi haline getiriyoruz:
fun <R : Any> R.logger(): Lazy<Logger> {
return lazy { Logger.getLogger(unwrapCompanionClass(this.javaClass).name) }
}
// see code for unwrapCompanionClass() below in "Putting it all Together section"
Bu kod ayrıca bir Companion Object içinde kullanırsanız, günlükçü adının sınıfın kendisinde kullandığınız gibi olmasını sağlar. Şimdi şunları yapabilirsiniz:
class Something {
val LOG by logger()
fun foo() {
LOG.info("Hello from Something")
}
}
sınıf başına veya sınıf başına bir örnekle daha statik olmasını istiyorsanız:
class SomethingElse {
companion object {
val LOG by logger()
}
fun foo() {
LOG.info("Hello from SomethingElse")
}
}
Ve foo()
her iki sınıfı da çağırmaktan elde ettiğiniz sonuç:
26 Aralık 2015 11:30:55 org.stackoverflow.kotlin.test. Bir şey için BİLGİ: Merhaba bir şey
26 Aralık 2015 11:30:55 org.stackoverflow.kotlin.test.SomethingElse foo BİLGİ: Merhaba SomethingElse
Eklenti İşlevleri (Herhangi bir ad alanının "kirliliği" nedeniyle bu durumda nadirdir)
Kotlin, bu kodun bazılarını daha da küçük hale getirmenize izin veren birkaç gizli numaraya sahiptir. Sınıflar üzerinde uzantı işlevleri oluşturabilir ve böylece onlara ek işlevler verebilirsiniz. Yukarıdaki yorumlardan bir öneri, Any
bir logger fonksiyonu ile genişletmekti . Bu, herhangi bir sınıftaki IDE'sinde kod tamamlamayı her kullandığında parazit oluşturabilir. Ancak, genişletmenin Any
veya başka bir işaretleyici arayüzünün gizli bir yararı vardır : kendi sınıfınızı genişlettiğinizi ve dolayısıyla içinde bulunduğunuz sınıfı tespit edebileceğinizi ima edebilirsiniz. Ha? Daha az kafa karıştırıcı olmak için kod İşte:
// extend any class with the ability to get a logger
fun <T: Any> T.logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
Şimdi bir sınıf (veya tamamlayıcı nesne) içinde, bu uzantıyı kendi sınıfımda çağırabilirim:
class SomethingDifferent {
val LOG = logger()
fun foo() {
LOG.info("Hello from SomethingDifferent")
}
}
Üretim üreten:
26 Aralık 2015 11:29:12 AM org.stackoverflow.kotlin.test.SomethingDifferent foo BİLGİ: Merhaba SomethingDifferent
Temel olarak, kod bir dahili numaraya çağrı olarak görülür Something.logger()
. Sorun şu ki, diğer sınıflarda "kirlilik" yaratarak aşağıdakiler de doğru olabilir:
val LOG1 = "".logger()
val LOG2 = Date().logger()
val LOG3 = 123.logger()
İşaretleyici Arabiriminde Uzantı İşlevleri (ne kadar yaygın olduğundan emin değil, ancak "özellikler" için ortak model)
Uzantıların kullanımını daha temiz hale getirmek ve "kirliliği" azaltmak için, aşağıdakileri genişletmek üzere bir işaretleyici arabirimi kullanabilirsiniz:
interface Loggable {}
fun Loggable.logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
Veya yöntemi, varsayılan bir uygulamayla arabirimin bir parçası haline getirin:
interface Loggable {
public fun logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
}
Ve sınıfınızda şu varyasyonlardan birini kullanın:
class MarkedClass: Loggable {
val LOG = logger()
}
Üretim üreten:
26 Aralık 2015 11:41:01 AM org.stackoverflow.kotlin.test.MarkedClass foFO BİLGİ: Merhaba MarkedClass'tan
Kaydediciyi tutmak için tek tip bir alan oluşturulmasını zorlamak istiyorsanız, bu arabirimi kullanırken, uygulayıcının kolayca aşağıdaki gibi bir alana sahip olmasını isteyebilirsiniz LOG
:
interface Loggable {
val LOG: Logger // abstract required field
public fun logger(): Logger {
return Logger.getLogger(unwrapCompanionClass(this.javaClass).name)
}
}
Şimdi arayüzün uygulayıcısı şöyle görünmelidir:
class MarkedClass: Loggable {
override val LOG: Logger = logger()
}
Tabii ki, soyut bir temel sınıf aynı şeyi yapabilir, hem arayüz hem de bu arayüzü uygulayan bir soyut sınıf seçeneği esneklik ve bütünlüğü sağlar:
abstract class WithLogging: Loggable {
override val LOG: Logger = logger()
}
// using the logging from the base class
class MyClass1: WithLogging() {
// ... already has logging!
}
// providing own logging compatible with marker interface
class MyClass2: ImportantBaseClass(), Loggable {
// ... has logging that we can understand, but doesn't change my hierarchy
override val LOG: Logger = logger()
}
// providing logging from the base class via a companion object so our class hierarchy is not affected
class MyClass3: ImportantBaseClass() {
companion object : WithLogging() {
// we have the LOG property now!
}
}
Hepsini Bir Araya Getirmek (Küçük bir yardımcı kütüphane)
Yukarıdaki seçeneklerden herhangi birinin kullanımını kolaylaştıracak küçük bir yardımcı kütüphane. Kotlin'de API'leri beğeninize göre daha fazla genişletmek yaygındır. Uzatma veya üst seviye işlevlerde. Aşağıda, günlüklerin nasıl oluşturulacağı konusunda seçenekler sunan bir karışım ve tüm varyasyonları gösteren bir örnek verilmiştir:
// Return logger for Java class, if companion object fix the name
fun <T: Any> logger(forClass: Class<T>): Logger {
return Logger.getLogger(unwrapCompanionClass(forClass).name)
}
// unwrap companion class to enclosing class given a Java Class
fun <T : Any> unwrapCompanionClass(ofClass: Class<T>): Class<*> {
return ofClass.enclosingClass?.takeIf {
ofClass.enclosingClass.kotlin.companionObject?.java == ofClass
} ?: ofClass
}
// unwrap companion class to enclosing class given a Kotlin Class
fun <T: Any> unwrapCompanionClass(ofClass: KClass<T>): KClass<*> {
return unwrapCompanionClass(ofClass.java).kotlin
}
// Return logger for Kotlin class
fun <T: Any> logger(forClass: KClass<T>): Logger {
return logger(forClass.java)
}
// return logger from extended class (or the enclosing class)
fun <T: Any> T.logger(): Logger {
return logger(this.javaClass)
}
// return a lazy logger property delegate for enclosing class
fun <R : Any> R.lazyLogger(): Lazy<Logger> {
return lazy { logger(this.javaClass) }
}
// return a logger property delegate for enclosing class
fun <R : Any> R.injectLogger(): Lazy<Logger> {
return lazyOf(logger(this.javaClass))
}
// marker interface and related extension (remove extension for Any.logger() in favour of this)
interface Loggable {}
fun Loggable.logger(): Logger = logger(this.javaClass)
// abstract base class to provide logging, intended for companion objects more than classes but works for either
abstract class WithLogging: Loggable {
val LOG = logger()
}
Saklamak istediklerinizi seçin ve kullanılan tüm seçenekler şunlardır:
class MixedBagOfTricks {
companion object {
val LOG1 by lazyLogger() // lazy delegate, 1 instance per class
val LOG2 by injectLogger() // immediate, 1 instance per class
val LOG3 = logger() // immediate, 1 instance per class
val LOG4 = logger(this.javaClass) // immediate, 1 instance per class
}
val LOG5 by lazyLogger() // lazy delegate, 1 per instance of class
val LOG6 by injectLogger() // immediate, 1 per instance of class
val LOG7 = logger() // immediate, 1 per instance of class
val LOG8 = logger(this.javaClass) // immediate, 1 instance per class
}
val LOG9 = logger(MixedBagOfTricks::class) // top level variable in package
// or alternative for marker interface in class
class MixedBagOfTricks : Loggable {
val LOG10 = logger()
}
// or alternative for marker interface in companion object of class
class MixedBagOfTricks {
companion object : Loggable {
val LOG11 = logger()
}
}
// or alternative for abstract base class for companion object of class
class MixedBagOfTricks {
companion object: WithLogging() {} // instance 12
fun foo() {
LOG.info("Hello from MixedBagOfTricks")
}
}
// or alternative for abstract base class for our actual class
class MixedBagOfTricks : WithLogging() { // instance 13
fun foo() {
LOG.info("Hello from MixedBagOfTricks")
}
}
Bu örnekte oluşturulan günlükçünün 13 örneğinin tümü aynı günlükçü adını ve çıktısını üretecektir:
26 Aralık 2015 11:39:00 AM org.stackoverflow.kotlin.test.MixedBagOfTricks foo BİLGİ: Hello from MixedBagOfTricks
Not:unwrapCompanionClass()
yöntem olmasını sağlar biz refakatçi nesne ziyade kapsayan sınıfının adıyla anılan bir logger üretemeyen. Tamamlayıcı nesneyi içeren sınıfı bulmak için önerilen geçerli yöntem budur. Tamamlayıcı nesnelere özel adlar verilebildiğinden , " $ Companion " ifadesini kullanarak adından removeSuffix()
çıkarmak işe yaramaz.