2014-09-21 75 views
1

我需要在我的应用程序中支持多个键盘布局。Scala将成员添加到基类

这是一个解决方案,但在斯卡拉我怀疑有一个更好的方法可以让我直接使用KeyLayout变量,例如输入KeyLayout.KEY_HOME而不是get_keycodes(12)("KEY_HOME")。这可能吗?如果我像下面的代码一样使用Map,那么我的其他代码将不断检查我想要使用的每个关键代码实际上是否存在,代码如get_keycodes(12).contains("KEY_HOME")。这将是好得多,如果get_keycodes(12)返回与多家val KEY_FOO = KeyCode(x)项目的对象,这样你就可以做的东西一样get_keycodes(12).KEY_HOME并让编译器保证,这是去上班

case class KeyCode(key: Int) 

object `KeyLayout_v12` extends KeyLayout { 
    val keys = Map("KEY_HOME" -> KeyCode(0x007a)) 
} 

object `KeyLayout_v15.2` extends KeyLayout { 
    val keys = Map("KEY_HOME" -> KeyCode(0x003a)) 
} 

class KeyLayout 

object KeyLayout { 

    // Gets the default keycodes for version you are using 
    def getKeycodes(version: Double): Map[String, KeyLayout] = androidVersion match { 
     case 12 => `KeyLayout_v12`.keys 
     case 15.2 => `KeyLayout_v15.2`.keys 
     case _ => { 
     info(s"Unknown $version, returning keycodes for 12") 
     `KeyLayout_v12`.keys 
     } 
    } 
} 
+0

我觉得类型类是你需要什么(编辑我的答案) – dk14 2014-09-21 07:07:16

回答

3

您可以使用类型类(反正你应该知道或编译时假设androidVesion):

trait Version 
object v12 extends Version 
object v15_2 extends Version 

class Layout[T <: Version] 

implicit class v12layout(v: Layout[v12.type]) {  
     val KEY_HOME = 100500 
} 

implicit class v15_2layout(v: Layout[v15_2.type]) { 
     val KEY_HOME = 100600 
     val KEY_A = 300900 
} 

def getLayout[T <: Version](v: T) = new Layout[T] 

getLayout(v15_2).KEY_HOME 

用法:

scala> getLayout(v12).KEY_HOME 
res4: Int = 100500 

scala> getLayout(v15_2).KEY_HOME 
res5: Int = 100600 

scala> getLayout(v12).KEY_A 
<console>:20: error: value KEY_A is not a member of Layout[v12.type] 
      getLayout(v12).KEY_A 
         ^

scala> getLayout(v15_2).KEY_A 
res7: Int = 300900 
+0

正是我在找的,谢谢! – Hamy 2014-09-21 23:48:32

0

为什么不使用嵌套的地图? val keyLayouts = Map[Double, Map[String, KeyCode]],然后获得keyLayouts.get(12).map(_.keys).getOrElse(//throw error or return default)的版本。

不知道我真的明白这个问题。

更新:

如果你想,就好像它在方法/值,那么你必须做起来很访问KEY_HOME。或者你可以使用Symbol s,那么你可以做keyLayouts(12)('KEY_HOME)

虽然还没有真正理解这个问题的重点,但也不明白为什么你在你的代码片段中包含getKeycodes,因为它看起来不相关。

+0

我想避免使用像'地图[字符串,邀请码]什么',而是能够返回一个包含'val KEY_FOO = KeyCode(xxx)'成员的对象。这意味着所有'KeyCode'用法都会在编译时被检查,并且您不必经常检查(以某种方式)您的地图是否包含“KeyCode”,例如'keyMap.contains(“KEY_HOME”)' – Hamy 2014-09-21 05:33:53

+0

基本上,我想用'keyLayouts'替换'keyLayouts.get(12).map(_ .key).getOrElse(//抛出错误或返回默认值)'' .get(12).KEY_HOME' – Hamy 2014-09-21 05:39:03

+0

@Hamy ive已更新,但您的问题仍然非常隐晦。 – samthebest 2014-09-21 06:02:11

0

这是没有提及另一种方法是scala.Dynamic特质。它允许在标记为Dynamic的类型实例上通过将它们转换为适当的applyDynamicselectDynamicupdateDynamic方法调用来调用任意方法。以下示例演示了可能的解

case class Key(name: String, code: Int) 

sealed trait Version 
sealed trait v12 extends Version 
sealed trait v15_2 extends Version 

sealed trait KeyLayout[T <: Version] extends Dynamic { 

    def keys: Set[Key] 

    def selectDynamic(name: String): Option[Key] = 
    keys.find(_.name == name) 
} 

object KeyLayout { 

    def apply[V <: Version: KeyLayout] = implicitly[KeyLayout[V]] 

    implicit object `12` extends KeyLayout[v12] { 
    val keys = Set(Key("KEY_HOME", 0x007a)) 
    } 

    implicit object `15_2` extends KeyLayout[v15_2] { 
    val keys = Set(Key("KEY_HOME", 0x003a)) 
    } 
} 

用法示例:

scala> KeyLayout[v15_2].KEY_HOME 
res2: Option[Key] = Some(Key(KEY_HOME,58)) 

scala> KeyLayout[v12].KEY_FOO 
res3: Option[Key] = None 
+0

在这种情况下没有编译时检查没有KEY_F00。另外,没有理由将密钥存储在Set中而不是Map(O(N)与O(1))中。 selectDynamic(即使使用Map)也会慢一点。 – dk14 2014-09-21 19:35:03

+0

,如果你使用动态 - 你并不需要implicits(代码可能不需要重写)并且不需要常量值,因为至少关键提取实际上正在运行到运行时。基于动态的KeyLayout [v12] .KEY_FOO和KeyLayout(12.0)(“KEY_FOO”)在使用方面没有区别。 – dk14 2014-09-21 19:54:11

+0

@ dk14,同意。这只是“Dynamic”用法的一个例子。我的意图是展示不同的方法。 – 4e6 2014-09-22 05:19:59