2009-03-24 1 views
1

Я задал довольно много вопросов о типах коллекции Scala и о том, как они могут быть использованы. Рассмотрим, как я мог бы написать некоторые службы API/реализация в Java:Дизайн Scala API; служба, возвращающая набор <I>, где я - некоторый интерфейс (аннотация/черта)

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

и теперь осущ:

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

} 

позволяет сказать, что есть очень хорошая причина, почему мне нужно иметь доступ в рамках реализации услуг к набору продуктов как ProductImpl s, а не IProduct s.

Похоже, что в Scala нет реального способа достижения этой цели, в то же время эксплицитно возвращая scala.collection.immutable.Set из метода доступа API. Если я не возражаю, возвращая копию набора.

я буду считать, что возвращение фактической копии данных плохая практика (не стесняйтесь спорить об этом!):

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

Так что поэтому мой единственный выбор реальный дизайн должен иметь API возвращает scala.collection.Set и использовать только для чтения вида:

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

Вашего последний блок кода более точно имитирует код Java вы эмуляцию: возвращающийся только для чтения вида изменяемого Set.

В этой ситуации, если ваша реализация поддержки является неизменной. Установите [ProductImpl], и вы хотите вернуть неизменяемый. Установить [IProduct], он безопасен для броска.

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

Но это бессмысленный актерский состав, не так ли? Это то, что дало бы мне безответное предупреждение в Java, не так ли? Является ли scala-компилятор проверкой безопасности типа? –

+0

Нет, компилятор Scala не проверяет безопасность приводов. В этом случае это безопасно. Чтобы убедиться в его безопасности, вы можете сделать следующее: productsByIsin.getOrElse (isin, immutable.Set.empty) .map (x => x: IProduct) , который проверяется компилятором, но он также создает новый карта. –

+0

На самом деле 'Set' в Scala инвариантны только потому, что 'Set [A]' также является '(A => Boolean)', а правила дисперсии для наследования функций точно противоположны правилам для 'List'. Вот почему вы можете взять «list: List [ProductImpl]» и назначить его «val products»: List [IProduct] = list' без приведения в действие, и хотя вы не можете сделать то же самое с инвариантом 'Set' s, это абсолютно безопасно бросать его в суперкласс - неизменяемые Наборы не могут быть повреждены таким образом. Альтернативным и более элегантным способом было бы использовать 'Set [ProductImpl](). ToSet [IProduct]', но он создаст новую коллекцию. – Sergey

Смежные вопросы