我们在尝试使用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对应接口。
答案 0 :(得分:1)
大多数用Scala编写但应该在Scala和Java中使用的框架都有一个单独的Java API 。例如,Akka,Play和Spark就属于这种情况。
这不是因为实例化Scala类完全不可能来自Java,而是因为它非常非惯用,繁琐且烦人。因此,设计人员只需创建一个单独的Java API,并一劳永逸地解决所有转换和工厂方法的问题,而不是将Java数据结构转换为Scala数据结构的负担转移给API的用户。
即代替
您执行以下操作:
foobar.api.java
包添加到您的库中,实现单独的Java API,仍使用Scala (pain-singleton) 这应该会大大减少你对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)提供可以从Java调用的fooWithProducts
版本
没有任何转换:
// in api.java
def fooWithProducts(ps: java.util.List[Product]): Foo =
// Convert java List to scala Seq here,
// call original `fooWithProducts`
对应该出现在API中的每个类和方法执行此操作。为了使其正常工作,API必须具有最小的表面积,并且与库的内部相比,可以集中并且相对简单。