How can I create an anonymous instance of a generic type?

Consider this simple class:

class MyClass(p: (String, String) *) { val params: Map[String, String] = p.toMap } 

And a class that extends it:

 class SomeOtherClass(p: (String, String) *) extends MyClass(p: _*) 

I would like to remove the constructor from MyClass in order to avoid transferring initialization parameters to everything that extends it (possibly many types). Instead, I would prefer companion type objects that extend MyClass to inherit some construction method.

 class MyClass { val param = Map.empty[String, String] // some member with a default value } trait MyClassBuilder[A <: MyClass] { def apply(p: (String, String) *): A = ??? } 

Basically apply should do something like this:

 new A { override val param = p.toMap } 

Obviously, the above code may not work. The idea is that the subtype will be used as follows:

 class SomeOtherClass extends MyClass object SomeOtherClass extends MyClassBuilder[SomeOtherClass] { // object specific things.. } 

Then SomeOtherClass relies on the inherited apply method to instantiate itself. It seems like something like this might be possible with reflection or macros, but I really don't know. To be clear, I want client code like SomeOtherClass to not require any constructor, so I want to explore creating anonymous classes in a generic type. It may not be the SomeOtherClass that is generated; it may be everything that extends MyClass .

+5
source share
3 answers

This may help you:

 import scala.reflect._ abstract class MyClassBuilder[A <: MyClass: ClassTag] { def apply() = classTag[A].runtimeClass.newInstance.asInstanceOf[A] } 

You may find that I did not pass (String, String) * here, since they are redundant to instantiate. Anyway, you have access to the whole instance of A here, so at least you can change the param after creating the instance. This is not exactly what you want - as you seem to be looking for a way to extend SomeOtherClass at runtime. However, creating a new type in scala is not possible, but you might think of it as a prototype - just take an instance of SomeOtherClass and mutate once inside apply() :

 import scala.reflect._ object Ctx { class MyClass { private[Ctx] var _param = Map.empty[String, String] def param = _param } abstract class MyClassBuilder[A <: MyClass: ClassTag] { def apply(p: (String, String) *) = { val i = classTag[A].runtimeClass.newInstance.asInstanceOf[A] i._param = Map(p: _*) i } } } scala> class MySpecialClass extends Ctx.MyClass defined class MySpecialClass scala> object MySpecialBuilder extends Ctx.MyClassBuilder[MySpecialClass] defined module MySpecialBuilder scala> MySpecialBuilder("A" -> "b") res12: MySpecialClass = MySpecialClass@2871ed4a scala> res12.param res13: scala.collection.immutable.Map[String,String] = Map(A -> b) 

Otherwise, you will have to deal with reflection in compilation time.

An interesting alternative is extensible records (Shapeless2) - they practically allow you to create a new type in parts, but you will have to solve with HList instead of a class:

 import shapeless._ ; import syntax.singleton._ ; import record._ import shapeless.ops.record.Updater import scala.reflect.runtime.universe._ val param = Witness("param") val someFunW = Witness("someFun") //defining classess (instead of "class MyClass") type Param = Map[String, String] with KeyTag[param.T, Map[String, String]] type SomeFun = (String => String) with KeyTag[someFunW.T, (String => String)] type MyClass = Param :: HNil type MySpecialClass = SomeFun :: MyClass def someFun(s: String) = s + "A" //defining default values (instead of "val param = ...") def newMyClass[T <: HList : TypeTag]: T = ( if (typeTag[T] == typeTag[MyClass]) (param ->> Map.empty[String, String]) :: HNil else if (typeTag[T] == typeTag[MySpecialClass]) (someFunW ->> someFun _) :: newMyClass[MyClass] else HNil ).asInstanceOf[T] //Defining builder def buildWithParam[T <: HList: TypeTag](p: Map[String, String])(implicit ev: Updater[T, Param]) = newMyClass[T] + ("param" ->> p) scala> buildWithParam[MySpecialClass](Map("a" -> "v")) res6: ... = <function1> :: Map(a -> v) :: HNil scala> res6("someFun").apply("a") res7: String = aA scala> buildWithParam[MyClass](Map("a" -> "v")) res8: ... = Map(a -> v) :: HNil 
+2
source

With type as the next attribute;

 trait MyType { def params: Map[String, String] } 

An instance can be created by a companion object:

 object MyType { def apply(pairs: (String, String)*): MyType = new MyType { val params = pairs.toMap } } //allows: MyType("a" -> "b", "c" -> "d") 
0
source

You can add a constructor without parameters to a subclass as follows:

 class SomeOtherClass(p: (String, String)*) extends MyClass(p: _*) { def this() = this(somedefaultvalueofp) } 

and then use a companion object to apply it to the external:

 object SomeOtherClass { def apply(p: (String, String)*): SomeOtherClass = new SomeOtherClass(p) } 

********** edited *****************

Now I understand this requirement. Thus, you can add a constructor without parameters to your MyClass :

 class MyClass(p: (String, String)*) { def this() = this(defaultvalueofp) } 

And create a class builder, as you mentioned:

 class MyClassBuilder[T <: MyClass] { def apply(p: (String, String)*): T = new T(p) } 
-1
source

Source: https://habr.com/ru/post/1214442/


All Articles