views:

175

answers:

1

I have asked quite a few questions about the Scala collection types and how they might actually be used. Consider how I might write some service API/implementation in Java:

public interface JavaProductAPI {
     public Set<IProduct> getProducts(String isin);
}

And now the impl:

public class JavaProductAPIImpl implements JavaProductAPI {
    private Map<String, Set<ProductImpl>> productsByIsin;

    public Set<IProduct> getProducts() {
        Set<ProductImpl> s = productsByIsin.get(isin);
        return s == null 
                   ? Collections.<IProduct>emptySet()
                   : Collections.<IProduct>unmodifiableSet(s);
    }

}

Lets say that there's a really good reason why I need to have access within the service implementation to the set of products as being of ProductImpls, not IProducts.

It seems that in Scala, there's no real way to achieve this whilst at the same time exlicitly returning an scala.collection.immutable.Set from the API access method. Unless I don't mind returning a copy of the set.

I'm going to assume that returning an actual copy of the data is bad practice (feel free to argue this point!):

val productsByIsin: Map[String, scala.collection.Set[ProductImpl]] = ...
def getProducts(isin: String): scala.collection.immutable.Set[IProduct] = {
  productsByIsin.get(isin) match {
     case Some(s) => scala.collection.immutable.Set(s toSeq :_*)
     case None => scala.collection.immutable.Set.empty
  } 
}

So that therefore my only real design choice is to have the API return a scala.collection.Set and use a read-only view:

val productsByIsin: Map[String, scala.collection.mutable.Set[ProductImpl]] = ...
def getProducts(isin: String): scala.collection.Set[IProduct] = {
  productsByIsin.get(isin) match {
     case Some(s) => s readOnly
     case None => scala.collection.Set.empty
  } 
}
+2  A: 

Your last block of code more closely mimics the Java code you're emulating: returning a read-only view of a mutable Set.

That said, in this situation, if your backing implementation is an immutable.Set[ProductImpl] and you want to return an immutable.Set[IProduct], it's safe to cast.

import scala.collection._

trait IProduct
class ProductImpl extends IProduct

val productsByIsin: immutable.Map[String, immutable.Set[ProductImpl]] =
  immutable.Map.empty
def getProducts(isin: String): immutable.Set[IProduct] =
  productsByIsin.getOrElse(isin, immutable.Set.empty).asInstanceOf[immutable.Set[IProduct]]
Jorge Ortiz
But it's a meaningless cast, isn't it? This is the sort of thing that would give me an unchecked warning in Java isn't it? Is the scala compiler checking type safety?
oxbow_lakes
No, the Scala compiler does not check the safety of casts. In this case it's safe. To ensure that it's safe you can do the following: productsByIsin.getOrElse(isin, immutable.Set.empty).map(x => x : IProduct)which is checked by the compiler, but it also creates a new map.
Jorge Ortiz