2011-08-08 11 views
33

Birkaç özellik ile birlikte bir temel sınıftan oluşan bir sınıf hiyerarşisi tasarlıyorum. Temel sınıf, çeşitli yöntemlerin varsayılan uygulamalarını sağlar ve özellikler, istiflenebilir özellikler/karışımlar olarak davranacak şekilde, abstract override aracılığıyla bazı yöntemleri seçici olarak geçersiz kılar. etki alanına bir tasarım bu iyi çalışıyor perspektiften ve haritalar itibaren Kalıtsal "yapıcı parametreleri" alarak özellikleri nasıl bildirilir?

Ben, ancak buradan (bir özelliği) vb bir yüklemi buradan (başka özelliği)

ile bir filtreleme işlevi artık ekleyebilir, böylece

Bazı özelliklerin örtük parametreler almasını istiyorum. Bu, tasarım perspektifinden hala mantıklı geldiğinden ve uygulamada kafa karıştırıcı olmasından dolayı mutluyum. Ancak, derleyiciyi onunla çalışmaya ikna edemiyorum.

Sorunun özü, örtük olarak işaretlenebilecekleri şekilde bir özellik için yapıcı argümanları sağlayamayacağım gibi görünüyor. Bir yöntem uygulaması içinde örtük parametreye başvurulması beklenen "örtülü değer bulamadı" mesajı ile derlenemedi; Ben

implicit val e = implicitly[ClassName] 

aracılığıyla yöntemle içinde kullanılabilir olmasının (pratikte, bu kapsamda, hep) inşaat aşamasından itibaren örtük "yaymak" çalıştı ama tanım ki (hiç şüphesiz olarak birçoğunuz bekliyoruz) aynı mesajla başarısız oldu.

Buradaki problem, derleyicinin, özelliğinin implicit ClassName bayrağın kendisinin imzasını etiketlemesi için ikna edemediğimi ve örtük sağlamak için arayanları (örneğin, özelliği bir nesneye karıştıran kişileri) zorlayabilmesidir. . Şu anda arayanlar yapıyor, ancak derleyici bu seviyede kontrol etmiyor.


belli implicits inşaat anda kullanılabilir olmasını gerektiren olarak bir özelliği işaretlemek için herhangi bir yolu var mı?

(Ve değilse, bu basitçe henüz uygulanmadı veya bu pratik değildir neden daha derin bir sebebi var mı?) Aslında

cevap

15

, ben oldukça sık önce bu istedim, ama sadece bu fikir geldi ettik . Sen çevirebilir için

trait T(implicit impl: ClassName) { 
    def foo = ... // using impl here 
} 

[EDITED: Orijinal versiyonu diğer yöntemler için örtülü erişim sağlamak vermedi]

trait T { 
    // no need to ever use it outside T 
    protected case class ClassNameW(implicit val wrapped: ClassName) 

    // normally defined by caller as val implWrap = ClassNameW 
    protected val implWrap: ClassNameW 

    // will have to repeat this when you extend T and need access to the implicit 
    import implWrap.wrapped 

    def foo = ... // using wrapped here 
} 
+0

Bu, arayanın anonim bir nesnede olsa da, açık bir şekilde 'implWrap' tanımlamasını yapmaz mı? (Değilse, nasıl ayarlandığını anlamıyorum; açıklamayı ister misiniz?) –

+0

Evet, ancak yorumu görün: eğer örtük kullanımı kullanmak istiyorsa, yalnızca “val implWrap = ClassNameW” yazabilir. Bunu yapmanın daha iyi bir yolunu görmüyorum: soruda bahsettiğiniz gibi, özellikler sadece _any_ yapıcı parametrelerine sahip değildir (örtük olarak işaretlenebilir). –

+7

Elbette, daha iyi bir çözüm görmekten çok memnun olurum. –

0

Böyle yapabileceğini:

abstract class C 

trait A { this: C => 
    val i: Int 
}  

implicit val n = 3 

val a = new C with A { 
    val i = implicitly[Int] 
} 

Ama İçinde herhangi bir nokta olup olmadığından emin değilim - örtülü değeri açık bir şekilde referans gösterebilirsiniz.

İstediğiniz şey, örnekte i uygulamasından kurtulmaktır, ancak kendiniz söylediğiniz gibi, sorunun özü, özelliklerin yapıcı parametrelerini almamasıdır - ister örtük olsun ister olmasın önemli değil

Bu sorunun olası bir çözüm zaten geçerli sözdizimine yeni bir özellik eklemek olacaktır: örtük kapsamında ise i derleyici tarafından uygulanacağına dair

trait A { 
    implicit val i: Int 
} 

.

11

Bu soruna birkaç kez rastladım ve aslında biraz sinir bozucu ama çok fazla değil. Soyut üyeler ve parametreler genellikle, aynı şeyi yapmanın avantajları ve dezavantajları ile iki alternatif yoldur; Özgün bir üyeye sahip olan özellikler için çok fazla rahatsızlık vermez, çünkü özelliği uygulamak için başka bir sınıfa ihtiyacınız vardır. *

Bu nedenle, özellik sınıfında soyut bir değer beyanına sahip olmanız gerekir, böylece uygulama sınıflarının bir örtük sağlaması gerekir. senin için. Aşağıdaki örneğe bakın - doğru derler ve verilen özellik uygulamak için iki yol gösterir:

trait Base[T] { 
    val numT: Ordering[T] 
} 
/* Here we use a context bound, thus cannot specify the name of the implicit 
* and must define the field explicitly. 
*/ 
class Der1[T: Ordering] extends Base[T] { 
    val numT = implicitly[Ordering[T]] 
    //Type inference cannot figure out the type parameter of implicitly in the previous line 
} 
/* Here we specify an implicit parameter, but add val, so that it automatically 
* implements the abstract value of the superclass. 
*/ 
class Der2[T](implicit val numT: Ordering[T]) extends Base[T] 

Ben göstermek temel fikir de Knut Arne Vedaa yanıtında mevcut, ama daha çarpıcı ve kullanışlı yapmaya çalıştı örnek, gereksiz özelliklerin kullanımını düşürür.

* Bu, özelliklerin neden parametre kabul edemediği değil - Ben bunu bilmiyorum. Sadece bu durumda sınırlamanın kabul edilebilir olduğunu tartışıyorum.

+2

Ancak, bu şekilde, 'Base [T] 'deki yöntemleri tanımlarken, örtük bir' Sipariş [T] 'ye erişemezsiniz. Ve eğer 'numT' örtük hale getirirseniz, _this_ problemini düzeltirsiniz, fakat 'num num = = dolaylı olarak [Sipariş [T]]' sonsuz bir döngü haline gelir. –

+1

Bu sorunu gidermek için 'Base' değiştirirseniz, Der1 yazamazsınız, ancak Der2 yine de çalışır - ve yine de eşdeğer iken Der1'den daha kompakttır. 'trait Temel [T] { örtülü değer numarası: Sipariş [T] } sınıfı Der2 [T] (örtülü değer numarası: Sipariş [T]) Base [T]' yi genişletir – Blaisorblade

0

Bunun mümkün olmadığı gibi, temel sınıf 'constructor'da örtük val bildirme seçeneğine gittim. Bu soruya işaret edildiği gibi, bu ideal değildir, fakat derleyiciyi tatmin eder ve pragmatik olarak, benim özel durumumda çok fazla bir yük değildir.

Yine de daha iyi bir çözümü varsa, bunu duymaktan ve kabul etmekten mutlu olurum.

7

Bu mümkün değil.

Ancak, implicitly ve Scala'nın tür çıkarımını, bunu olabildiğince ağrısız hale getirmek için kullanabilirsiniz.

trait MyTrait { 

    protected[this] implicit def e: ClassName 

} 

ve Özlü sonra

class MyClass extends MyTrait { 

    protected[this] val e = implicitly // or def 

} 

ve hatta uzanan sınıfta türünü yazma gerektirmez.