2009-03-24 16 views
1

J'ai posé quelques questions sur les types de collection Scala et comment ils pourraient être réellement utilisés. Considérez comment je pourrais écrire une API de service/implémentation en Java:Scala API design; un service qui renvoie un ensemble <I> où je suis une interface (résumé/trait)

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

Et maintenant 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); 
    } 

} 

Disons que il y a une très bonne raison pour laquelle je dois avoir accès dans la mise en œuvre de services à l'ensemble des produits comme étant de ProductImpl s, pas IProduct s.

Il semble que dans Scala, il n'y a pas de véritable moyen d'y parvenir tout en retournant explicitement un scala.collection.immutable.Set à partir de la méthode d'accès API. Sauf si cela ne me dérange pas de retourner une copie de l'ensemble.

Je vais supposer que renvoyer une copie réelle des données est une mauvaise pratique (ne hésitez pas à faire valoir ce 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 
    } 
} 

Alors que, par conséquent mon seul choix de conception réelle est d'avoir la API renvoie une scala.collection.Set et utiliser une vue en lecture seule:

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 
    } 
} 

Répondre

2

Votre dernier bloc de code plus près imite le code Java vous émule: le retour d'une vue en lecture seule d'un ensemble mutable. Cela dit, dans ce cas, si votre implémentation de sauvegarde est un immutable.Set [ProductImpl] et que vous voulez retourner un immutable.Set [IProduct], il est sûr de lancer.

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]] 
+0

Mais c'est un casting sans signification, n'est-ce pas? C'est le genre de chose qui me donnerait un avertissement non contrôlé en Java n'est-ce pas? Le compilateur scala vérifie-t-il la sécurité du type? –

+0

Non, le compilateur Scala ne vérifie pas la sécurité des moulages. Dans ce cas, c'est sûr. Pour s'assurer que c'est sûr, vous pouvez faire ce qui suit: productsByIsin.getOrElse (isin, immutable.Set.empty) .map (x => x: IProduct) qui est vérifié par le compilateur, mais il crée également un nouveau carte. –

+0

En fait, 'Set's dans Scala ne sont invariants que parce qu'un' Set [A] 'est aussi' (A => Boolean) ', et les règles de variance pour l'héritage des fonctions sont exactement à l'opposé de celles de' List's. C'est pourquoi vous pouvez prendre 'list: List [ProductImpl]' et l'assigner à 'val products: List [IProduct] = list' sans cast, et bien que vous ne puissiez pas faire la même chose avec' Set's invariant, c'est absolument sûr de le lancer dans une super-classe - les ensembles immuables ne peuvent pas être corrompus de cette façon. Une manière alternative et plus élégante serait d'utiliser 'Set [ProductImpl](). ToSet [IProduct]', mais cela créera une nouvelle collection. – Sergey