#scala #inheritance #extends #case-class
Вопрос:
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
val details = person.asInstanceOf[Details] // ???
println(details) // I want only Details class fields
У меня есть эти 2 занятия. На самом деле у обоих есть много областей. Где-то мне нужно только поле суперкласса, взятое из класса Персон.
Есть хороший способ получить только значения суперкласса и не сопоставлять их поле за полем?
*Я почти уверен, что у меня возникнут некоторые проблемы с написанием json для сведений о классе (который не является классом вариантов и не имеет одноэлементного объекта, но это другая тема)
Комментарии:
1. Ваш
println(details)
на самом делеprintln(details.toString)
, и этаtoString
реализация исходит от самогоclass
instance
. Вот, собственноclass
, что у васinstance
естьPerson
, так что оно это и будет использовать. Именно так работает наследование. Кроме того, в вашем классеDetails
нет никаких полей. Вы можете проверить , создав экземплярDetails
byval d = new Details(1, "Europe")
, к которому вы не сможете получить доступd.age
, илиd.address
поскольку в нем нет элемента поля.2. может быть, это сработает?
val details = person.getClass.getSuperclass
?3. @Бен, который даст вам супер-класс времени выполнения
Person
. Как вы думаете, что можно сделать с помощью класса времени выполнения ?4. @AlleXys можете ли вы опубликовать пример того, каким должен быть ожидаемый результат?
5. Что значит «получить поля»? Просто распечатайте их содержимое?
Ответ №1:
Если я правильно понял ваш вопрос, то вы, возможно, спрашиваете меня о полиморфизме времени выполнения или отправке динамического метода из java. Если это так, вам, возможно, придется создать как класс, так и класс без регистра
class Details( val age: Int, val address: String)
class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address) {
}
Теперь создайте объект person и ссылку на суперкласс (Подробности)
val detail:Details = new Person("Alex", 33, "Europe")
println(detail.address)
println(detail.age)
Таким образом, вы сможете получить единственную address
и age
Другой способ-это , например, почему мы не можем создать Details
отдельную сущность, такую как:
case class Details( age: Int, address: String)
case class Person(name: String,
details: Details
)
val detail = Person("Alex", Details(10,"Europe") )
Выход:
println(detail.details)
Details(10,Europe)
Комментарии:
1. Поскольку у меня есть таблица mysql с более чем 80 столбцами, и я пытался создать сущность (или домен), у меня возникла эта проблема. Итак, мой репозиторий возвращает класс с 80 полями (точно так же, как поля таблицы БД). Чтобы не изменять его, я думаю, что мне нужно будет вручную отобразить результат репо в новом классе case с деталями в виде вложенного класса внутри Person (это будет служебное задание).
2. Да, старайтесь всегда сохранять вложенную структуру для сгруппированных данных, таким образом, анализ JSON также будет для вас легким.
3. Я проверил ответ Амита, потому что нашел проблему в своей логике, и его ответ решил ее. Мне нужно сравнить предыдущее значение класса Person с новым значением (запросы бд каждые 30 секунд). Но в Scala, похоже, я не могу сравнить 2 экземпляра класса (с
==
илиequals()
), поэтому мне нужно сопоставить его в другом классе case с вложенными полями (вложенными объектами), а затем сравнить значения. Потрясающая Scala 😀 Я обнаружил, что могу переопределить метод equals, но я этого не хочу.4. Мне не нужно вручную проверять каждое поле (
if new.field1 == old.field2 amp;amp; new.field2 == old.field2 amp;amp; ..
и так далее). В javascript можно сделать что-то подобноеclass.fields.forEach(field => newClass[field] == oldClass[field])
. Это что-то из отражения в Scala и сейчас слишком далеко от меня :d5. Да, это верно, но в конечном итоге вы доберетесь туда со всем синтаксическим сахаром scala. Счастливого обучения, приятель! 🙂
Ответ №2:
Я опубликую решение, которое использует макросистему scala (старого типа, а не новейшую, представленную в Scala 3.0). Для тебя это может быть перебором…
Кстати, если вы хотите получить доступ только к родительским значениям (например, для получения ключа, пары значений), вы можете:
- учитывая тег типа, получите всех родителей;
- из них извлеките все средства доступа (vals);
- для каждого значения получите его значение;
- и, наконец, возвращает список со всеми выбранными способами доступа
Поэтому я стараюсь решать каждый пункт шаг за шагом. Прежде всего, мы должны записать определение макроса как:
object Macros {
def accessors[T](element : T): String = macro MacrosImpl.accessors[T]
}
object MacrosImpl {
def accessors[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[String] = ...
}
для первого пункта мы можем использовать API макропрограммирования отражения, используя c.universe
:
import c.universe._
val weakType = weakTypeTag[T] //thanks to the WeakTypeTag typeclass
val parents = weakType.tpe.baseClasses
для второго пункта мы можем повторить родительские классы, а затем использовать только общедоступные методы доступа:
val accessors = parents
.map(weakType.tpe.baseType(_))
.flatMap(_.members)
.filter(_.isPublic)
.filter(_.isMethod)
.map(_.asMethod)
.filter(_.isAccessor)
.toSet
Так, например, если мы напишем Macros.accessors[Details](person)
, accessors
получится age
и address
.
Чтобы получить эту ценность, мы можем использовать квазигаутинг. Итак, сначала мы берем только имя значений:
val names = accessors
.map(_.fullName)
.map(_.split("\."))
.map(_.reverse.head)
Затем мы преобразуем их в TermName
:
val terms = names.map(TermName(_))
И, наконец, мы преобразуем каждый термин в кортеж значений ключа, содержащий имя val и его значение:
val accessorValues = terms
.map(name => c.Expr[(String, Any)](q"(${name.toString}, ${element}.${name})"))
.toSeq
Последний шаг состоит в преобразовании a Seq[Expr[(String, Any)]
в a Expr[Seq[(String, Any)]
. Способ сделать это может заключаться в использовании рекурсии reify
и splicing
выражения:
def seqToExprs(seq: Seq[Expr[(String, Any)]]): c.Expr[Seq[(String, Any)]] =
seq.headOption match {
case Some(head) =>
c.universe.reify(
Seq((head.splice._1, head.splice._2))
seqToExprs(seq.tail).splice
)
case _ => c.Expr[Seq[(String, Any)]](q"Seq.empty")
}
Поэтому теперь я решаю вернуть строковое представление (но вы можете манипулировать им так, как хотите).:
val elements = seqToExprs(accessorValues)
c.Expr[String](q"${elements}.mkString")
Вы можете использовать его как:
import Macros._
class A(val a : Int)
class B(val b : Int) extends A(b)
class C(val c: Int) extends B(c)
//println(typeToString[List[Set[List[Double]]]])
val c = new C(10)
println(accessors[C](c)) // prints (a, 10)(b, 10)(c, 10)
println(accessors[B](c)) // prints (a, 10)(b, 10)
println(accessors[A](c)) // prints (a, 10)
И, используя ваш пример:
// Your example:
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
println(accessors[Details](person)) // prints (address,Europe)(age,33)
println(accessors[Person](person)) // prints (address,Europe)(age,33)(name,Alex)
Здесь есть репозиторий с реализованным макросом.
Scala 3.0 представляет более безопасную и чистую макросистему, если вы используете ее и хотите пойти дальше, вы можете прочитать эти статьи: