java - Scala:使用类型参数信息访问静态 Java 方法

标签 java scala generics type-inference type-parameter

我使用 Java 代码生成器(不幸的是我无法摆脱它),它会输出如下代码:

abstract class Abs1 { //... }
abstract class Abs2 { //... }
interface I { //... }

public static final class C1 extends Abs implements I {

  public final static Inner newInstance() { return Inner.create(); }

  public final class Inner extends Abs2 { 
    private static Inner create() { return new Inner(); }
  }

  public final static C1 build() { 
    // builds the object instantiating some fields with defaults
  }

}

final static class C2 extends Abs implements I {

  // exactly same as in C1: 

  public final static Inner newInstance() { return Inner.create(); }

  public final class Inner extends Abs2 { 
    private static Inner create() { return new Inner(); }
  }

  public final static C1 build() { 
    // builds the object instantiating some fields with defaults
  }

}

我还有许多其他类似风格的类(class),例如 C1 和 C2。 如您所见,每个外部类(C1、C2 ...)都有一个内部类,始终命名为 Inner。此外,所有内部类都扩展相同的抽象类。

我想从字符串创建每个外部类的实例。我有一个实用程序(合并),可以将实例与其字符串合并。

def createC1(str: String) = {
  val ins = C1.newInstance
  Merger(ins, str)
  ins.build
}

def createC2(str: String) = {
  val ins = C2.newInstance
  Merger(ins, str)
  ins.build
}

...这似乎是明显的代码重复。所以我想利用类型参数化。

def build[A <: Abs](str: String) = {
  val ins = A.newInstance // this obviously won't compile - but this is my intent
  Merger(ins, str)
  ins.build
}

所以我可以这样做:build[Cn](str)

如何使用类型参数 info 调用静态 Java 方法?我尝试使用 ClassTag:

def build[A <: Abs : ClassTag](str: String) = {
    val ins1 = (new A) // ins1 type is now A with Object, WAT ?
    val ins2 = ins.asInstanceOf[A] // I do not want to do asInstanceOf but the compiler won't recognize that ins2 is an instance of A otherwise
    // ins2 has access to newInstance method
    // Not sure if the below code works, I had to actually try it :)
    // Merger(str, ins2)
    // ins2.build()
    ???
}

为什么它没有推断出 ins1 中的类型?

编辑:使外部类静态。

最佳答案

问题

您尝试创建的代码的主要问题是您尝试访问泛型类型的静态方法。 Java 和 Scala 中的泛型类型都不允许访问与该类型关联的静态方法。我读过一些相当复杂的反射,您可能可以使用它们来访问泛型类型的companion对象,但我强烈建议避免任何事情就像在这种情况下一样,这样的代码非常脆弱且难以维护。毕竟,任何抽象的目标都是使代码更易于使用,而不是更难。

几个解决方案

您可以轻松地执行一些操作来抽象这种情况并使使用此代码更容易处理。 我假设您根本无法修改生成的代码,因为如果可以的话,有一些甚至更好的方法来处理这个问题我要跳过。

解决方案 1 结构类型

此问题的第一个也是最简单的解决方案是使用结构类型。考虑以下代码(它是 Scala 代码,但在概念上它在 Java 中的功能应该相同)。

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Foo
trait InnerFoo
trait Bar extends Foo
trait Baz extends Foo
object Bar {
    class InnerBar extends InnerFoo
    def newInstance: InnerBar = new InnerBar
    def build: Bar = new Bar {}
}
object Baz {
    class InnerBaz extends InnerFoo
    def newInstance: InnerBaz = new InnerBaz
    def build: Baz = new Baz {}
}

// Exiting paste mode, now interpreting.

defined trait Foo
defined trait InnerFoo
defined trait Bar
defined trait Baz
defined object Bar
defined object Baz

现在,我想对具有以下函数的任何内容调用 newInstance,这不会编译

def makeInstance[A <: Foo, B <: InnerFoo]: B = {
  A.newInstance
  // do some stuff
  A.build
}

这是因为编译器不知道 AFoo 的子类型,而 A 的伴生对象具有其上的 newInstance 方法。类/特征的伴生对象与类/特征本身的类型层次结构无关。这在 Java 中也基本相同,类上的静态成员与类的类型无关,它们只是定义作用域为类中的成员的地方。具体方式。

但是,如果我像这样定义 makeInstance 函数,则一切正常,

scala> def makeInstance[A <: InnerFoo, B <: Foo](static: { def newInstance: A; def build: B }) = {
     | static.newInstance
     | // do some stuff
     | static.build
     | }
warning: there were two feature warnings; re-run with -feature for details
makeInstance: [A <: InnerFoo, B <: Foo](static: AnyRef{def newInstance: A; def build: B})B

scala> makeInstance(Bar)
res16: Bar = Bar$$anon$1@1601e47

scala> makeInstance(Baz)
res17: Baz = Baz$$anon$2@6de54b40

这仅比您的理想解决方案多稍微样板代码。但请注意,编译器为此发出警告的原因是结构类型需要反射,因此会降低运行时性能。但是,如果代码不在程序的关键部分中,您可能不必担心它。

解决方案2,只需传递函数即可

我们可以直接传递函数来完成工作,而不是使用结构类型。这比结构类型版本稍微更详细,但也更快、更安全。

scala> def makeInstance[A <: InnerFoo, B <: Foo](newInstanceProc: => A, buildProc: => B): B = {
     | newInstanceProc
     | // do some stuff
     | buildProc
     | }
makeInstance: [A <: InnerFoo, B <: Foo](newInstanceProc: => A, buildProc: => B)B

scala> makeInstance(Bar.newInstance, Bar.build)
res19: Bar = Bar$$anon$1@4d0402b

关于java - Scala:使用类型参数信息访问静态 Java 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32729009/

相关文章:

java - 如何在Scala中移植java内部函数?

java - Bean 验证应用于接口(interface)类型

javascript - 想要显示选择的重复排名以及使用 javascript/jquery 重复的排名

java - intellij idea 在 : import org. apache.spark.{SparkContext, SparkConf} 上出现 scala 错误

java - 如何将所有 .class 文件从 .jar 文件转换为 .java 文件

events - 等待akka actor系统可用

java - 使用 Java 客户端 API 在 MarkLogic 中获取所有文档 URI

scala - Scala 中的词性标注

typescript - 在 TypeScript 中,如何从基类返回子类?

java - 具有相同删除的两种方法不一定是等效的(或者它们之间的签名不是子签名)?