2015-03-02 14 views
5

xem xét lớp học này rất đơn giản:Làm thế nào tôi có thể xây dựng một cá thể ẩn danh của một loại chung?

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

Và một lớp mà kéo dài nó:

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

Tôi muốn loại bỏ các nhà xây dựng từ MyClass để tránh mang theo các thông số khởi tạo trong tất cả mọi thứ mà kéo dài nó (nhiều loại tiềm năng). Thay vào đó, tôi muốn các đối tượng đồng hành của các loại mở rộng MyClass để kế thừa một số loại phương thức trình xây dựng.

class MyClass { 
    val param = Map.empty[String, String] // some member with a default value 
} 

trait MyClassBuilder[A <: MyClass] { 
    def apply(p: (String, String) *): A = ??? 
} 

Về cơ bản apply nên làm một cái gì đó như thế này:

new A { 
    override val param = p.toMap 
} 

Rõ ràng, các mã trên không thể làm việc. Ý tưởng là một sub-type sẽ được sử dụng như thế này:

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

Sau đó SomeOtherClass sẽ dựa vào sự thừa hưởng apply phương pháp để tạo thể hiện của bản thân. Có vẻ như một cái gì đó như thế này có thể là có thể với sự phản ánh hoặc macro, nhưng tôi thực sự không có ý tưởng. Để rõ ràng, tôi muốn mã máy khách như SomeOtherClass không yêu cầu hàm tạo nào cả, đó là lý do tại sao tôi muốn khám phá việc tạo các lớp ẩn danh trên một kiểu chung. Có thể không phải là SomeOtherClass được tạo, nó có thể là bất cứ thứ gì mở rộng MyClass.

+0

tôi đặt cược một macro là cách để đi ở đây, mà sẽ sản xuất các boilerplate bạn đang cố gắng để tránh –

Trả lời

2

Điều này có thể giúp bạn:

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

Bạn có thể thấy rằng tôi đã không vượt qua (String, String) * ở đây vì chúng không cần thiết cho việc khởi tạo. Dù sao, bạn có quyền truy cập vào toàn bộ ví dụ của A ở đây, vì vậy ít nhất bạn có thể thay đổi param sau khi khởi tạo. Nó không chính xác những gì bạn muốn - như bạn có vẻ tìm kiếm một cách để bổ sung mở rộng SomeOtherClass trong thời gian chạy.Tuy nhiên, sự sáng tạo thời gian chạy của loại mới trong scala là không thể, nhưng bạn có thể nghĩ về nó như một nguyên mẫu - chỉ mất SomeOtherClass dụ và đột biến một lần bên 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 = [email protected] 

scala> res12.param 
res13: scala.collection.immutable.Map[String,String] = Map(A -> b) 

Nếu không, bạn phải đối phó với thời gian biên dịch phản ánh.

Một thay thế thú vị là extensible records (Shapeless2) - họ thực tế cho phép bạn xây dựng các loại mảnh mới bằng mảnh, nhưng bạn sẽ phải đối phó với HList thay vì lớp có:

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 
+0

* "Thời gian chạy tạo kiểu mới trong scala là không thể" * - Đó là khá nhiều câu tôi cần. Điều này rất hữu ích và mang lại cho tôi nhiều ý tưởng để làm việc. –

+0

Tôi quan tâm đến các kiểm tra bình đẳng cho các thẻ loại ở đây - 'typeTag [T] == typeTag [MyClass]'. Không khó để kết thúc bằng hai loại thẻ khác nhau cho cùng loại cơ bản. –

0

Với loại có đặc điểm sau;

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

Một ví dụ có thể được tạo ra bởi các đối tượng đồng:

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

Tôi nghĩ rằng anh ta hỏi làm thế nào để làm điều đó với một chung –

+0

Thật vậy, tôi đang cố gắng tránh mang theo boilerplate của phương pháp 'áp dụng' cho các loại phụ . –

-1

Bạn có thể thêm một constructor không tham số trong các lớp con như thế này:

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

và sau đó sử dụng đối tượng đồng với áp dụng để áp dụng giá trị từ bên ngoài:

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

********** đã chỉnh sửa ****************

Tôi hiểu bây giờ là yêu cầu. Vì vậy, bạn có thể thêm các nhà xây dựng không tham số trên MyClass của bạn:

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

Và tạo một người thợ xây lớp như bạn đề cập:

class MyClassBuilder[T <: MyClass] { 
    def apply(p: (String, String)*): T = new T(p) 
} 
Các vấn đề liên quan