2016-06-25 1 views
4

내가 이렇게 내 parse 기능을 사용하기위한 일반 대수 데이터 형식을 정의하려면 :일반 대수 데이터 형식에서 멤버 형식에 'T'가 필요한 이유는 무엇입니까?

sealed class Result<T> { 
    class Success(val value: T, val pos: Int) : Result<T>() 
    class Failure(val message: String, val pos: Int) : Result<T>() 
} 

fun <T> parse(t: Parser<T>, input: String, initialPos: Int = 0, collectErrors: Boolean = true) : Result<T> { 

그러나이 T으로 허용되지 않습니다 다음 정의되지 않은 참조입니다.

나는 그것이 작동하는 모든 구성원 유형에 T를 추가하는 경우 : 나 여기에

sealed class Result<T> { 
    class Success<T>(val value: T, val pos: Int) : Result<T>() 
    class Failure<T>(val message: String, val pos: Int) : Result<T>() 
} 

날 내가 여기서 뭔가를 놓치고 생각하게하는 다소 혼란이다. 첫 번째 경우에 멤버 유형을 정의 할 때 T이 표시되지 않는 이유는 무엇입니까? 내가 기대 Success의 인스턴스를 생성 또한

이 구문은하기 :

Result<T>.Success<T>(tv.someValue, pos) 

하지만 그 대신 작동하지 않습니다 내가 할이 :

Result.Success<T>(tv.someValue, pos) 

이것은 바람직 구문 나에게 나는 왜 내가 여기에 결과에 T을 남겨둬야하는지 이해하기 위해 고투하고있다.

답변

6

Result는 T라는 이름의 하나의 일반적인 매개 변수를 사용하여, 일반적인 클래스입니다 : 당신이 Foo의 인스턴스가 필요합니다

open class Foo<T> { 
    inner class Bar : Foo<T>() 
} 

Bar를 인스턴스화 :이 방법에는 다음과 같은 유형의 계층 구조를 구성 할 수 있습니다 클래스 이름은 Result이지만 Result<T>이 아닙니다.

Success도 제네릭 클래스입니다. 따라서 일반용이므로 Success<T>으로 정의해야합니다. 그렇지 않으면 더 이상 제네릭이 아닙니다. Result의 서브 클래스 (generic)인데도 비 제네릭 타입이 될 수 있습니다. 예를 들면 다음과 같습니다.

class Success(val value: String, val pos: Int) : Result<String>() 

결과 및 실패는 일반적이지만 모든 것에 대해 일반 유형을 사용하지 않습니다. 그래서 사실 지금

sealed class Result { 
    class Success<T>(val value: T, val pos: Int) : Result() 
    class Failure(val message: String, val pos: Int) : Result() 
} 

으로 클래스를 정의 할 수 있습니다, 당신은 왜 Result.Success<T>(tv.someValue, pos)하지 Result<T>.Success<T>(tv.someValue, pos)를 사용해야합니까?

클래스 이름이 Result.Success이기 때문에. 매개 변수 유형은 클래스 이름의 일부가 아닙니다.이 추정되기 때문에 대부분의 시간, 그것은 전혀 지정할 필요는 없습니다 :

val r = Result.Success("foo", 1) 

Success<String>의 인스턴스를 만듭니다. 당신이 Success<CharSequence>를 만드는 대신 원하는 경우에, 당신은 명시 적으로 제네릭 형식을 지정해야합니다 : 귀하의 예에서

val r = Result.Success<CharSequence>("foo", 1) 

또는

val r: Result.Success<CharSequence> = Result.Success("foo", 1) 
+1

감사합니다. 'Result'는 엄격하게 generic 일 필요는 없지만,'parse'는'Success ' generic 타입이'Parser '와 같은'Result'를 만들어내는 것을 좋아합니다. 결과 I에 'T'를 포함하지 않으면 실수로 API가 유형 안전성이 떨어질 것이라고 생각합니다. – FuleSnabel

+0

'fun foo() : Result '와 같은 메소드 서명을 원한다면? 그래서 반환 값은 성공 또는 실패입니까? – miguel

4

규칙은 Java의 규칙과 동일합니다. 기본적으로 Result의 정적 중첩 클래스 인 SuccessFailure이 있습니다. Kotlin에는 "멤버 유형"이 없으며 정적 중첩 클래스는 외부 클래스의 범위에 액세스 할 수있는 일반 클래스입니다. 클래스가 일반 수퍼 클래스를 확장하면 항상 generic 형식 매개 변수를 바인딩해야합니다.

대조적으로 정적이 아닌 중첩 클래스 (inner 키워드로 표시됨)는 항상 외부 클래스의 제네릭 형식 공 간도를 전달합니다.

val b = Foo<String>().Bar() 
+0

감사합니다. 'T'가 범위의 일부가 될 것이라고 생각하기 때문에'T'가 정적 인 내부 클래스에 표시되지 않는 이유는 아직도 다소 당황 스럽습니다. – FuleSnabel

+0

Java에서 작동하는 방식이며 Kotlin에서도 동일하게 작동합니다. 다르게 작동하는 언어에서 왔습니까? –

+1

@FuleSnabel 정적 중첩 클래스 (정적 내부 클래스와 같은 것은 없습니다)는 최상위 클래스와 다르지 않습니다. 유일한 차이점은 더 이상한 이름 (Success 대신 Result.Success)이 있고, 클래스를 포함하는 private 멤버에 액세스 할 수 있다는 것입니다 (반대의 경우도 마찬가지입니다). 그 외, 그들은 최상위 클래스와 같습니다. 내부 클래스는 소유자 ** 인스턴스 **에 대한 암시 적 참조를 가지고 있기 때문에 다릅니다. –

0

세 가지 일반적인 매개 변수없는 일이있다. 나는. 코드는 동일합니다 :

sealed class Result<I> { 
    class Success<A>(val value: A, val pos: Int) : Result<A>() 
    class Failure<B>(val message: String, val pos: Int) : Result<B>() 
} 

그러나 다른 답변 언급하는 방법, 당신이 사용하지 않는 IB 매개 변수, 그래서 그들은 생략 할 더 낫다.

+0

'value' 속성은'T' 타입입니다. 코드가 컴파일되지 않습니다. –

+0

@KirillRakhman 감사합니다! – voddan

0

이 같은 밖으로 분산을 사용하는 경우 그것은 작동해야

sealed class Result<out T> { 
    data class Success<out T>(val value: T, val pos: Int) : Result<T>() 
    data class Failure(val message: String, val pos: Int) : Result<Nothing>() 
} 
관련 문제