데이터 클래스는 Java의 구식 POJO를 대체하는 것으로 보입니다. 이러한 클래스가 상속을 허용 할 것으로 예상되지만 데이터 클래스를 확장하는 편리한 방법은 없습니다. 내가 필요한 것은 다음과 같습니다.
open data class Resource (var id: Long = 0, var location: String = "")
data class Book (var isbn: String) : Resource()
component1()
메소드 의 충돌로 인해 위의 코드가 실패 합니다. data
클래스 중 하나에 만 주석을 남겨두면 작동하지 않습니다.
아마도 데이터 클래스를 확장하는 또 다른 관용구가 있습니까?
UPD : 자식 자식 클래스에만 주석을 달 수 있지만 data
주석은 생성자에 선언 된 속성 만 처리합니다. 즉, 모든 부모의 속성을 선언 open
하고 무시해야합니다.
open class Resource (open var id: Long = 0, open var location: String = "")
data class Book (
override var id: Long = 0,
override var location: String = "",
var isbn: String
) : Resource()
답변
진실은 데이터 클래스가 상속과 잘 어울리지 않는다는 것입니다. 데이터 클래스의 상속을 금지하거나 심각하게 제한하는 것을 고려하고 있습니다. 예를 들어, equals()
비추 상 클래스의 계층 구조에서 올바르게 구현할 수있는 방법이없는 것으로 알려져 있습니다.
따라서 내가 제공 할 수있는 모든 것 : 데이터 클래스에 상속을 사용하지 마십시오.
답변
생성자 외부의 수퍼 클래스에서 속성을 추상으로 선언하고 하위 클래스에서 재정의합니다.
abstract class Resource {
abstract var id: Long
abstract var location: String
}
data class Book (
override var id: Long = 0,
override var location: String = "",
var isbn: String
) : Resource()
답변
위의 추상 클래스를 사용하는 솔루션은 실제로 해당 클래스를 생성하고 데이터 클래스가 해당 클래스에서 확장되도록합니다.
추상 클래스를 원하지 않으면 인터페이스 를 사용하는 것이 어떻습니까?
Kotlin의 인터페이스는 이 기사에 표시된대로 속성 을 가질 수 있습니다 .
interface History {
val date: LocalDateTime
val name: String
val value: Int
}
data class FixedHistory(override val date: LocalDateTime,
override val name: String,
override val value: Int,
val fixedEvent: String) : History
Kotlin이 이것을 어떻게 컴파일하는지 궁금했습니다. Intellij [Kotlin bytecode] 기능을 사용하여 생성 된 동등한 Java 코드는 다음과 같습니다.
public interface History {
@NotNull
LocalDateTime getDate();
@NotNull
String getName();
int getValue();
}
public final class FixedHistory implements History {
@NotNull
private final LocalDateTime date;
@NotNull
private final String name;
private int value;
@NotNull
private final String fixedEvent;
// Boring getters/setters as usual..
// copy(), toString(), equals(), hashCode(), ...
}
보시다시피, 일반 데이터 클래스와 똑같이 작동합니다!
답변
@ Željko Trogrlić의 답변이 정확합니다. 그러나 추상 클래스에서와 동일한 필드를 반복해야합니다.
또한 추상 클래스 내에 추상 서브 클래스가있는 경우 데이터 클래스에서 이러한 추상 서브 클래스에서 필드를 확장 할 수 없습니다. 먼저 데이터 서브 클래스 를 만든 다음 필드를 정의해야합니다.
abstract class AbstractClass {
abstract val code: Int
abstract val url: String?
abstract val errors: Errors?
abstract class Errors {
abstract val messages: List<String>?
}
}
data class History(
val data: String?,
override val code: Int,
override val url: String?,
// Do not extend from AbstractClass.Errors here, but Kotlin allows it.
override val errors: Errors?
) : AbstractClass() {
// Extend a data class here, then you can use it for 'errors' field.
data class Errors(
override val messages: List<String>?
) : AbstractClass.Errors()
}
답변
코 틀린 특성이 도움이 될 수 있습니다.
interface IBase {
val prop:String
}
interface IDerived : IBase {
val derived_prop:String
}
데이터 클래스
data class Base(override val prop:String) : IBase
data class Derived(override val derived_prop:String,
private val base:IBase) : IDerived, IBase by base
샘플 사용법
val b = Base("base")
val d = Derived("derived", b)
print(d.prop) //prints "base", accessing base class property
print(d.derived_prop) //prints "derived"
이 접근법은 @Parcelize의 상속 문제에 대한 해결 방법이 될 수도 있습니다.
@Parcelize
data class Base(override val prop:Any) : IBase, Parcelable
@Parcelize // works fine
data class Derived(override val derived_prop:Any,
private val base:IBase) : IBase by base, IDerived, Parcelable
답변
비 데이터 클래스에서 데이터 클래스를 상속 할 수 있습니다. 상속시 컴파일러 생성 데이터 클래스 메서드를 일관되고 직관적으로 작동시킬 수있는 방법이 없기 때문에 다른 데이터 클래스에서 데이터 클래스를 상속 할 수 없습니다.
답변
equals()
계층 구조에서 올바르게 구현 하는 것은 실제로 피클이지만 여전히 다른 방법 (예 🙂 상속을 지원하는 것이 좋습니다 toString()
.
좀 더 구체적으로 설명하자면, 다음과 같은 구조를 가지고 있다고 가정 해 봅시다 toString()
.
abstract class ResourceId(open val basePath: BasePath, open val id: Id) {
// non of the subtypes inherit this... unfortunately...
override fun toString(): String = "/${basePath.value}/${id.value}"
}
data class UserResourceId(override val id: UserId) : ResourceId(UserBasePath, id)
data class LocationResourceId(override val id: LocationId) : ResourceId(LocationBasePath, id)
우리의 가정 User
및 Location
단체가 (자신의 적절한 리소스 ID를 반환 UserResourceId
및 LocationResourceId
호출, 각각을) toString()
하나에서 ResourceId
: 모든 하위 유형에 대한 일반적으로 유효 아주 좋은 작은 표현 될 수 있습니다 /users/4587
, /locations/23
하위 유형의 비는 오버라이드 (override) 상속 때문에, 등 불행히도 toString()
으로부터 방법 추상 기본은 ResourceId
, 호출 toString()
사실은 덜 꽤 표현 결과 : <UserResourceId(id=UserId(value=4587))>
,<LocationResourceId(id=LocationId(value=23))>
위의 모델을 모델링하는 다른 방법이 있지만 이러한 방법을 사용하면 비 데이터 클래스를 사용하도록 강요하거나 (데이터 클래스의 많은 이점을 생략 함) toString()
모든 데이터 클래스에서 구현을 복사 / 반복하게 됩니다. (상속하지 않음).
