Здесь необходимо учитывать две вещи.
Во-первых, существует проблема «ничего». Как вы цепляете вещи, когда часть цепочки может ничего не возвращать? Ответ использует понимание Option
и for
. Например:
scala> case class Address(city: Option[String] = None, street: Option[String] = None, number: Option[Int] = None)
defined class Address
scala> case class Contact(name: String, phone: Option[String] = None, address: Option[Address] = None)
defined class Contact
scala> case class ContactDetails(phone: Option[String] = None, address: Option[Address] = None)
defined class ContactDetails
scala> case class Contact(phone: Option[String] = None, address: Option[Address] = None)
defined class Contact
scala> case class Person(name: String, contactDetails: Option[Contact] = None)
defined class Person
scala> case class Company(name: String, contactPerson: Option[Person] = None)
defined class Company
scala> val p1 = Company("ABC", Some(Person("Dean", Some(Contact(None, Some(Address(city = Some("New England"))))))))
p1: Company = Company(ABC,Some(Person(Dean,Some(Contact(None,Some(Address(Some(New England),None,None)))))))
scala> val p2 = Company("Finnicky", Some(Person("Gimli", None)))
p2: Company = Company(Finnicky,Some(Person(Gimli,None)))
scala> for(company <- List(p1, p2);
| contactPerson <- company.contactPerson;
| contactDetails <- contactPerson.contactDetails;
| address <- contactDetails.address;
| city <- address.city) yield city
res28: List[String] = List(New England)
Так вы должны писать код, который может возвращать что-то или нет в Scala.
Вторая проблема, конечно, заключается в том, что иногда у вас может не быть доступа к исходному коду для правильного преобразования. В этом случае есть некоторые дополнительные синтаксические издержки для заголовка, если не может быть использован неявный. Ниже я приведу пример, в котором я использую функцию "toOption
" - в Scala 2.8 есть такая вещь, о которой я расскажу ниже.
scala> def toOption[T](t: T): Option[T] = if (t == null) None else Some(t)
toOption: [T](t: T)Option[T]
scala> case class Address(city: String = null, street: String = null, number: Int = 0)
defined class Address
scala> case class Contact(phone: String = null, address: Address = null)
defined class Contact
scala> case class Person(name: String, contactDetails: Contact = null)
defined class Person
scala> case class Company(name: String, contactPerson: Person = null)
defined class Company
scala> val p1 = Company("ABC", Person("Dean", Contact(null, Address(city = "New England"))))
p1: Company = Company(ABC,Person(Dean,Contact(null,Address(New England,null,0))))
scala> val p2 = Company("Finnicky", Person("Gimli"))
p2: Company = Company(Finnicky,Person(Gimli,null))
scala> for(company <- List(p1, p2);
| contactPerson <- toOption(company.contactPerson);
| contactDetails <- toOption(contactPerson.contactDetails);
| address <- toOption(contactDetails.address);
| city <- toOption(address.city)) yield city
res30: List[String] = List(New England)
Помните, что вы можете быть весьма изобретательны в названии функции. Таким образом, вместо «toOption
» я мог бы назвать его «?
», в этом случае я бы написал что-то вроде «?(address.city)
».
Спасибо Nuttycom за напоминание, в Scala 2.8 на объекте Option
есть фабрика Option
, поэтому я могу просто написать Option(something)
. По сути, вы можете заменить "toOption
" выше на "Option
". И если вы предпочитаете использовать ?
, вы можете просто использовать import
с переименованием.