繁体   English   中英

如何将Scala案例类和集合桥接到Java

[英]How to bridge Scala case classes and collections to Java

当尝试使用Java的Scala代码时,我们遇到了问题。 我们正在寻找一种自动桥接各种Scala和java集合的方法,而无需显式地在我们的Java代码中进行显式转换。 当很少的地方时,这样做是合理的,但是如果它分布广泛并且不断添加新的scala类,那么这将变得不切实际。

例如,在我们的Scala代码中,我们有一些这样的类(其中很多):

case class Product(id: String, group: String, attributes : Map[String,String])

def fooWithProducts(products : Seq[Product]) = ...

// create a product
val p1 = Product("id1","group1", Map("attribute1" -> "value1","attribute2" -> "value2")

val p2 = Product("id2","group1", Map("attribute1" -> "value1","attribute2" -> "value2")

fooWithProducts(Seq(p1,p2))

当我们想用Java编写类似的代码时,它变得非常麻烦,并且我们需要在各处添加许多辅助样板:

// JAVA
// create a product
HashMap<String, String> attributes = new HashMap<String, String>();
attributes.put("my-attribute","my-value");

// create a scala collection
scala.collection.immutable.Map<String, String> scalaAttributes = Conversions.toScalaImmutableMap(attributes);

// create a product
Product p = Product("id","group",scalaAttributes)

// create a scala sequence of product
ArrayList<Product> dataFeedsProducts = new ArrayList<Product>();
        dataFeedsProducts.add(Product);
Seq<Product> seq = JavaConversions.asScalaBuffer(dataFeedsProducts).seq();

fooWithProducts(seq)

// SCALA helpers

public class Conversions {
    //https://stackoverflow.com/questions/11903167/convert-java-util-hashmap-to-scala-collection-immutable-map-in-java/11903737#11903737
    @SuppressWarnings("unchecked")
    /**
     * Transforms a java map to a scala immutable map
     */
    public static <K, V> scala.collection.immutable.Map<K, V> toScalaImmutableMap(java.util.Map<K, V> javaMap) {
        final java.util.List<scala.Tuple2<K, V>> list = new java.util.ArrayList(javaMap.size());
        for (final java.util.Map.Entry<K, V> entry : javaMap.entrySet()) {
            list.add(scala.Tuple2.apply(entry.getKey(), entry.getValue()));
        }
        final scala.collection.Seq<Tuple2<K, V>> seq = scala.collection.JavaConverters.asScalaBufferConverter(list).asScala().toSeq();
        return (scala.collection.immutable.Map<K, V>) scala.collection.immutable.Map$.MODULE$.apply(seq);
    }

    /**
     * transforms a scala map to the java counterpart
     */
    public static <K,V> java.util.Map<K,V> mapAsJavaMap(scala.collection.immutable.Map<K,V> m) {
        if (m == null) {
            return null;
        }
        return JavaConversions.mapAsJavaMap(m);
    }

    public static<A> Seq<A> asScalaSeq(java.util.List<A> l) {
        return JavaConversions.asScalaBuffer(l).toSeq();
    }
}

我们想知道是否有一种方法可以为这些Scala惯用法注释Scala代码或自动生成等效的Java对应接口。

大多数用Scala编写但应该同时用于Scala和Java的框架具有单独的Java API 例如,Akka,Play和Spark就是这种情况。

之所以这样做,是因为从Java实例化Scala类是完全不可能的,而是因为它非常惯用,繁琐且令人讨厌。 因此,设计人员无需将负担将Java数据结构转换为Scala数据结构的负担交给API用户,而是只需创建一个单独的Java API,并一劳永逸地解决所有转换和工厂方法的问题。

也就是说,代替

  • 在Scala中编写库并仅提供scala API
  • 使用Scala API用Java编写C_1类,并在转换中污染代码(painful-1)
  • ...
  • 使用Scala API用Java编写C_n类,并通过转换污染代码(painful-n)

您可以执行以下操作:

  • 在Scala中编写一个库,首先提供Scala API
  • foobar.api.java包添加到您的库中, 实现单独的Java API,仍然使用Scala (pain-singleton)
  • 使用Java API (nice-and-pleasant-1)用Java编写C_1类
  • ...
  • 使用Java API用Java编写类C_n (nice-and-pleasant-n)

这将大大减少您给scala库用户带来的痛苦(在您的情况下:您自己)。

对于您提供的微小代码段,具体来说,这意味着:

1)提供工厂方法,以便可以方便地从Java实例化Product

// somewhere in api.java
def product(
  id: String, 
  grp: String, 
  attrToVal: java.util.HashMap[String, String]
): Product = /* use `JavaConversions`, invoke constructor */

2)提供fooWithProducts的版本,可以从Java调用它而无需进行任何转换:

// in api.java
def fooWithProducts(ps: java.util.List[Product]): Foo = 
  // Convert java List to scala Seq here, 
  // call original `fooWithProducts`

对应该出现在API中的每个类和方法执行此操作。 为了使其正常工作,与库的内部结构相比,API必须具有最小的表面积,并且要集中且相对简单。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM