Подтвердить что ты не робот

Как смешивать черту с примером?

Учитывая черту MyTrait:

trait MyTrait {
  def doSomething = println("boo")
}

его можно смешать в классе с extends или with:

class MyClass extends MyTrait

Его также можно смешивать при создании экземпляра нового экземпляра:

var o = new MyOtherClass with MyTrait
o.doSomething

Но... может ли атрибут (или любой другой, если это имеет значение) добавить к существующему экземпляру?

Я загружаю объекты с использованием JPA в Java, и я хотел бы добавить к ним некоторые функции, используя черты. Возможно ли вообще?

Я хотел бы иметь возможность смешать в признаке следующим образом:

var o = DBHelper.loadMyEntityFromDB(primaryKey);
o = o with MyTrait //adding trait here, rather than during construction
o.doSomething
4b9b3361

Ответ 1

У меня есть идея для этого использования:

//if I had a class like this
final class Test {
  def f = println("foo")
}
trait MyTrait {
  def doSomething = {
    println("boo")
  }
}
object MyTrait {
  implicit def innerObj(o:MixTest) = o.obj

  def ::(o:Test) = new MixTest(o)
  final class MixTest private[MyTrait](val obj:Test) extends MyTrait
}

вы можете использовать этот признак, как показано ниже:

import MyTrait._

val a = new Test
val b = a :: MyTrait
b.doSomething
b.f

для вашего примера кода:

val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething

Надеюсь, это поможет вам.

ОБНОВЛЕНО

object AnyTrait {
  implicit def innerObj[T](o: MixTest[T]):T = o.obj

  def ::[T](o: T) = new MixTest(o)
  final class MixTest[T] private[AnyTrait](val obj: T) extends MyTrait
}

но этот шаблон имеет некоторое ограничение, вы не можете использовать какой-то неявный вспомогательный метод, который уже определен.

val a = new Test
a.f
val b = a :: AnyTrait
b.f1
b.f
val c = "say hello to %s" :: AnyTrait
println(c.intern)  // you can invoke String method 
println(c.format("MyTrait"))  //WRONG. you can't invoke StringLike method, though there defined a implicit method in Predef can transform String to StringLike, but implicit restrict one level transform, you can't transform MixTest to String then to StringLike.
c.f1
val d = 1 :: AnyTrait
println(d.toLong)
d.toHexString // WRONG, the same as above
d.f1

Ответ 2

Существующий объект времени выполнения в JVM имеет определенный размер в куче. Добавление признака к нему означало бы изменение его размера в куче и изменение его подписи.

Таким образом, единственный способ пойти - это сделать какое-то преобразование во время компиляции.

Состав смеси в Scala происходит во время компиляции. Компилятор потенциально может создать оболочку B вокруг существующего объекта A с тем же типом, который просто перенаправляет все вызовы на существующий объект A, а затем смешивается с признаком T от B. Это, однако, не реализовано. Это может быть вопросом, когда это было бы возможно, поскольку объект A может быть экземпляром конечного класса, который не может быть расширен.

Таким образом, композиция mixin невозможна в существующих объектных экземплярах.

ОБНОВЛЕНО:

В связи с умным решением, предложенным Гогол-Шаном, и обобщая его для работы с любым признаком, это насколько я понял. Идея состоит в том, чтобы извлечь общую функциональность mixin в черту DynamicMixinCompanion. Затем клиент должен создать объект-компаньон, расширяющий DynamicMixinCompanion для каждого признака, для которого он хочет иметь динамическую функциональность mixin. Этот объект-компаньон требует определения создания анонимного объекта-объекта (::).

trait DynamicMixinCompanion[TT] {                                                                    
  implicit def baseObject[OT](o: Mixin[OT]): OT = o.obj                                              

  def ::[OT](o: OT): Mixin[OT] with TT                                                               
  class Mixin[OT] protected[DynamicMixinCompanion](val obj: OT)                                      
}                                                                                                    

trait OtherTrait {                                                                                   
  def traitOperation = println("any trait")                                                          
}                                                                                                    

object OtherTrait extends DynamicMixinCompanion[OtherTrait] {                                        
  def ::[T](o: T) = new Mixin(o) with OtherTrait                                                     
}                                                                                                    

object Main {                                                                                        
  def main(args: Array[String]) {                                                                    
    val a = "some string"                                                                            
    val m = a :: OtherTrait                                                                          
    m.traitOperation                                                                                 
    println(m.length)                                                                                
  }                                                                                                  
}                                                                                                    

Ответ 3

Я обычно использовал implicit для смешивания нового метода с существующим объектом.

См., если у меня есть код, как показано ниже:

final class Test {
  def f = "Just a Test"
  ...some other method
}
trait MyTrait {
  def doSomething = {
    println("boo")
  }
}
object HelperObject {
  implicit def innerObj(o:MixTest) = o.obj

  def mixWith(o:Test) = new MixTest(o)
  final class MixTest private[HelperObject](obj:Test) extends MyTrait
}

а затем вы можете использовать метод MyTrait с уже существующим тестом объекта.

val a = new Test
import HelperObject._
val b = HelperObject.mixWith(a)
println(b.f)
b.doSomething

в вашем примере, вы можете использовать вот так:

import HelperObject._
val o = mixWith(DBHelper.loadMyEntityFromDB(primaryKey));
o.doSomething

Я рассматриваю синтаксис префекта, чтобы определить этот объект HelperObject:

trait MyTrait {
  ..some method
}
object MyTrait {
  implicit def innerObj(o:MixTest) = o.obj

  def ::(o:Test) = new MixTest(o)
  final class MixTest private[MyTrait](obj:Test) extends MyTrait
}
//then you can use it
val a = new Test
val b = a :: MyTrait
b.doSomething
b.f
// for your example
val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething

Ответ 4

Как насчет неявного класса? Мне кажется легче для меня по сравнению с другими ответами с окончательным внутренним классом и функцией "mixin".

trait MyTrait {

    def traitFunction = println("trait function executed")

}

class MyClass {

    /**
     * This inner class must be in scope wherever an instance of MyClass
     * should be used as an instance of MyTrait. Depending on where you place
     * and use the implicit class you must import it into scope with
     * "import mypackacke.MyImplictClassLocation" or
     * "import mypackage.MyImplicitClassLocation._" or no import at all if
     * the implicit class is already in scope.
     * 
     * Depending on the visibility and location of use this implicit class an
     * be placed inside the trait to mixin, inside the instances class,
     * inside the instances class' companion object or somewhere where you
     * use or call the class' instance with as the trait. Probably the
     * implicit class can even reside inside a package object. It also can be
     * declared private to reduce visibility. It all depends on the structure
     * of your API.
     */
    implicit class MyImplicitClass(instance: MyClass) extends MyTrait

    /**
     * Usage
     */
    new MyClass().traitFunction

}