나는 Option[T]
Scala에서 수업 의 요점을 이해할 수 없습니다 . 내 말은, 나는 None
over의 어떤 이점도 볼 수 없다는 것을 의미 null
합니다.
예를 들어 다음 코드를 고려하십시오.
object Main{
class Person(name: String, var age: int){
def display = println(name+" "+age)
}
def getPerson1: Person = {
// returns a Person instance or null
}
def getPerson2: Option[Person] = {
// returns either Some[Person] or None
}
def main(argv: Array[String]): Unit = {
val p = getPerson1
if (p!=null) p.display
getPerson2 match{
case Some(person) => person.display
case None => /* Do nothing */
}
}
}
이제 방법, 가정 getPerson1
반환 null
후 전화가 만든 display
의 첫 번째 줄에 main
실패 할 수밖에 없다 NPE
. 마찬가지로 getPerson2
returns None
이면 display
유사한 오류와 함께 호출이 다시 실패합니다.
그렇다면 왜 Scala Option[T]
는 Java에서 사용되는 간단한 접근 방식을 따르는 대신 새로운 값 래퍼 ( )를 도입하여 문제를 복잡하게 만들 까요?
최신 정보:
@Mitch 의 제안에 따라 코드를 편집했습니다 . 나는 여전히의 특별한 이점을 볼 수 없습니다 Option[T]
. 예외적 인 경우 null
또는 None
두 경우 모두 테스트해야합니다 . 🙁
@Michael의 회신 에서 올바르게 이해했다면 유일한 장점은 이 메서드가 None을 반환 할 수 있다고Option[T]
프로그래머에게 명시 적으로 알려주는 것입니다 . 이것이이 디자인 선택의 유일한 이유입니까?
답변
Option
절대로 절대 .NET을 사용하지 않도록 강요하면 더 나은 점을 얻을 수 있습니다 get
. 그 이유 get
는 “ok, 나를 null-land로 돌려 보내줘”와 동일 하기 때문 입니다.
그래서 당신의 예를 들어보십시오. display
사용하지 않고 어떻게 전화 get
하겠습니까? 다음은 몇 가지 대안입니다.
getPerson2 foreach (_.display)
for (person <- getPerson2) person.display
getPerson2 match {
case Some(person) => person.display
case _ =>
}
getPerson2.getOrElse(Person("Unknown", 0)).display
이 대안 중 display
어떤 것도 존재하지 않는 것을 호출하도록 허용 하지 않습니다.
왜 get
존재 하는지에 관해서 Scala는 코드 작성 방법을 알려주지 않습니다. 그것은 당신을 부드럽게 자극 할 수 있지만, 안전망이없는 상태로 돌아가고 싶다면 그것은 당신의 선택입니다.
여기에 못 박았습니다.
Option [T]의 유일한 장점은이 메서드가 None을 반환 할 수 있다는 것을 프로그래머에게 명시 적으로 알려주는 것입니다.
“전용”을 제외하고. 그러나 다른 방식으로 다시 말씀 드리겠습니다 . over 의 주요 이점 은 형식 안전성입니다. 컴파일러가 허용하지 않으므로 존재하지 않을 수있는 객체에 메서드를 전송 하지 않도록합니다.Option[T]
T
T
두 경우 모두 null 허용 여부를 테스트해야한다고 말했지만 잊어 버렸거나 모르는 경우 null을 확인해야합니다. 컴파일러가 알려줄까요? 아니면 사용자가?
물론 Java와의 상호 운용성 때문에 Scala는 Java와 마찬가지로 null을 허용합니다. 따라서 Java 라이브러리를 사용하거나 잘못 작성된 Scala 라이브러리를 사용하거나 잘못 작성된 개인 Scala 라이브러리 를 사용하는 경우 여전히 널 포인터를 처리해야합니다.
Option
제가 생각할 수있는 다른 두 가지 중요한 이점 은 다음과 같습니다.
-
문서 : 메소드 유형 서명은 객체가 항상 반환되는지 여부를 알려줍니다.
-
모나 딕 구성 가능성.
후자는 완전히 이해하는 데 훨씬 오래 걸리며 복잡한 코드에서만 강점을 보여주기 때문에 간단한 예제에는 적합하지 않습니다. 그래서 아래에 예를 들어 보 겠지만, 이미받은 사람들을 제외하고는 거의 의미가 없다는 것을 잘 알고 있습니다.
for {
person <- getUsers
email <- person.getEmail // Assuming getEmail returns Option[String]
} yield (person, email)
답변
비교:
val p = getPerson1 // a potentially null Person
val favouriteColour = if (p == null) p.favouriteColour else null
와:
val p = getPerson2 // an Option[Person]
val favouriteColour = p.map(_.favouriteColour)
스칼라에서 맵 함수 로 나타나는 모나 딕 속성 bind를 사용하면 객체가 ‘null’인지 여부에 대한 걱정없이 객체에 대한 작업을 연결할 수 있습니다.
이 간단한 예를 좀 더 살펴 보겠습니다. 사람들 목록에서 가장 좋아하는 색상을 모두 찾고 싶다고 가정 해 보겠습니다.
// list of (potentially null) Persons
for (person <- listOfPeople) yield if (person == null) null else person.favouriteColour
// list of Options[Person]
listOfPeople.map(_.map(_.favouriteColour))
listOfPeople.flatMap(_.map(_.favouriteColour)) // discards all None's
또는 아버지의 어머니의 여동생 이름을 찾고 싶을 수도 있습니다.
// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister
// with options
val fathersMothersSister = getPerson2.flatMap(_.father).flatMap(_.mother).flatMap(_.sister)
나는 이것이 옵션이 어떻게 삶을 좀 더 쉽게 만들 수 있는지에 대한 약간의 빛을 비추 길 바랍니다.
답변
차이는 미묘합니다. 진정한 함수라는 점을 명심하십시오 . 값을 반환 해야합니다. null은 그런 의미에서 실제로 “일반 반환 값”으로 간주되지 않으며 더 낮은 유형의 / nothing입니다.
그러나 실질적인 의미에서 선택적으로 무언가를 반환하는 함수를 호출하면 다음을 수행합니다.
getPerson2 match {
case Some(person) => //handle a person
case None => //handle nothing
}
물론, null과 비슷한 것을 할 수 있습니다. 그러나 이것은 getPerson2
그것이 반환된다는 사실로 인해 호출의 의미를 분명 하게 만듭니다 Option[Person]
(다른 사람이 문서를 읽고 NPE를 읽지 않기 때문에 NPE를 얻는 것 외에는 좋은 실용적인 것입니다. 문서).
나는 내가 할 수있는 것보다 더 엄격한 대답을 줄 수있는 기능적인 프로그래머를 찾으려고 노력할 것이다.
답변
나에게 옵션은 이해 구문으로 처리 할 때 정말 흥미 롭습니다. 촬영 synesso 앞의 예 :
// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister
// with options
val fathersMothersSister = for {
father <- person.father
mother <- father.mother
sister <- mother.sister
} yield sister
할당 중 하나가 None
인 경우 fathersMothersSister
는 None
이지만 no NullPointerException
는 발생합니다. 그런 다음 fathersMothersSister
걱정없이 Option 매개 변수를 사용하는 함수에 안전하게 전달할 수 있습니다 . 그래서 당신은 null을 확인하지 않고 예외를 신경 쓰지 않습니다. 이것을 synesso 예제에 제시된 Java 버전과 비교하십시오 .
답변
Option을 사용하면 매우 강력한 구성 기능이 있습니다.
def getURL : Option[URL]
def getDefaultURL : Option[URL]
val (host,port) = (getURL orElse getDefaultURL).map( url => (url.getHost,url.getPort) ).getOrElse( throw new IllegalStateException("No URL defined") )
답변
다른 누군가가 이것을 지적했을 수도 있지만 나는 그것을 보지 못했습니다.
Option [T] 대 null 검사를 사용한 패턴 일치의 한 가지 장점은 Option이 봉인 된 클래스이므로 Some 또는 None 케이스를 코딩하지 않으면 Scala 컴파일러가 경고를 발행한다는 것입니다. 경고를 오류로 바꾸는 컴파일러 플래그가 컴파일러에 있습니다. 따라서 런타임이 아닌 컴파일 타임에 “존재하지 않음”사례를 처리하지 못하는 것을 방지 할 수 있습니다. 이것은 null 값을 사용하는 것보다 큰 이점입니다.
답변
null 검사를 피하는 데 도움이되는 것이 아니라 강제로 null 검사를 수행하는 것입니다. 클래스에 10 개의 필드가 있고 그 중 2 개는 null 일 수있을 때 요점이 분명해집니다. 그리고 시스템에는 50 개의 다른 유사한 클래스가 있습니다. Java 세계에서는 정신적 기능, 명명 규칙 또는 주석을 조합하여 해당 필드에서 NPE를 방지하려고합니다. 그리고 모든 Java 개발자는이 점에서 상당한 수준으로 실패합니다. Option 클래스는 코드를 이해하려는 개발자에게 “nullable”값을 시각적으로 명확하게 할뿐만 아니라 컴파일러가 이전에 말하지 않은이 계약을 시행 할 수 있도록합니다.
