Это очень специфический запрос, поэтому позвольте мне изложить гипотетический случай, прежде чем я углублюсь в код.
Предположим, что у вас много одинаковых строк в таблице. Чтобы отличить одну строку от другой, мы хотели бы добавить в потенциально столбец со случайными числами, который может помочь предотвратить перекос данных. Например:
--------------------------
| MyClass1 |
--------------------------
| value1 | value2 | skew |
--------------------------
| 4 | 6 | 4962 |
--------------------------
| 4 | 6 | 6510 |
--------------------------
| 500 | 700 | 0 |
--------------------------
| 500 | 700 | 0 |
--------------------------
В этом случае перекос помогает предотвратить скопление данных в одном месте.
Можно сделать следующее:
case class MyClass1(value1: Int, value2: Int, skew: Int)
и создание экземпляра перекоса будет оставлено вызывающей стороне, то есть:
MyClass1(value1 = 4, value2 = 6, skew = ComplexFunc(value1, value2))
Однако предположим, что у меня есть много классов, которым нужно это значение перекоса. В этом случае наличие реализации для каждого из них будет подвержено ошибкам, а также требует, чтобы вызывающая сторона знала о значении перекоса. Таким образом, я хочу скрыть значение перекоса и его реализацию следующим образом:
case class SkewClass(skew: Int)
object SkewClass {
def apply(skewCondition: Boolean) : SkewClass = {
if (skewCondition) SkewClass(RandomInt()) else SkewClass(0)
}
}
case class MyClass1 extends SkewClass(
val value1 : Int = 0
val value2 : Int = 0
val skew : Int = this.apply(value1 != 500 && value2 != 700)
}
Короче говоря, я хочу иметь возможность создать экземпляр MyClass1 с потенциально случайным целым числом, добавленным в конце, называемым «перекосом». то есть:
scala> val x = MyClass1(value1 = 500, value2 = 700)
x: MyClass1 = MyClass1(500, 700, 0)
scala> val y = MyClass1(value1 = 52, value2 = 63)
y: MyClass1 = MyClass1(52, 63, 5347)
scala>
Очевидно, что приведенный выше код не компилируется (я все еще новичок в scala), но есть ли способ изменить этот код, чтобы можно было прикрепить это случайное целое число?
@BogdanVakulenko Извините, я не думаю, что выразился достаточно ясно. Я отредактировал вопрос, чтобы быть более явным
Можете ли вы объяснить, почему вы хотите различать одинаковые строки? Могут быть и другие способы сделать это (например, поле count
, а не несколько строк), поэтому это похоже на вопрос XY.
@ Тим, этот случай гипотетический, поэтому не думайте, что это именно то, что я ищу :)
@BogdanVakulenko уже дал ответ на ваш вопрос тл;др, поэтому непонятно, что еще вы ищете.
@Tim Функция RandomInt - это просто пример - я хочу иметь сложную функцию, которая вычисляет значение перекоса на основе других параметров дочернего класса, например: MyClass1(value1: Int, value2: Int, skew: Int = ComplexFunc(value1, value2))
, но я также хочу иметь возможность делать MyClass2(value1: Int, value2: Int, value3: Int, skew: Int = ComplexFunc(value1, value2, value2)
@ Тим, я добавил дополнительное уточнение к вопросу - теперь он гораздо более конкретен.
@GeoffreySaunders Я сомневаюсь, что это возможно реализовать так, как вы просили, потому что вам нужно skew
как собственное поле класса case, но вы не можете расширить класс case за классом case. Таким образом, кажется, что единственный способ скрыть поле skew
от вызывающей стороны — это реализовать метод apply
в сопутствующем объекте для каждого из ваших новых классов case, потому что это единственное место, где доступны все параметры, и вы можете использовать их для передачи вам ComplexFunc .
Должно ли значение skew
влиять на проверку равенства для класса? Если я создам val z = MyClass1(value1 = 52, value2 = 63)
, должно ли y == z
быть true
или false
, или это не имеет значения?
Я не совсем уверен, что вы здесь ищете, но на основе вашего описания это то, что я получил:
class SkewedClass(values: Int*) {
lazy val skew: Int = {
values.sum * 1234
}
}
case class MyClass1(value1: Int, value2: Int) extends SkewedClass(value1, value2)
case class MyClass2(value1: Int, value2: Int, value3: Int) extends SkewedClass(value1, value2, value3)
Замените реализацию перекоса на любую сложную функцию, которую вы хотите использовать.
В этом случае вы хотите, чтобы все ваши структуры имели общее свойство Skew
, но не обязательно какие-либо другие детали. Лучшее решение — просто использовать здесь трейт.
trait Skew {
def skewCondition: Boolean
lazy val skew: Int = if (skewCondition) RandomInt() else 0
}
case class AlwaysSkew(v1: Int, v2: Int) extends Skew {
override val skewCondition: Boolean = true
}
case class ConditionalSkew(v1: Int, v2: Int) extends Skew {
override val skewCondition: Boolean = (v1 != 500) && (v2 != 700)
}
val x = ConditionalSkew(500, 700)
val y = ConditionalSkew(1234,5678)
x.skew //0
y.skew //A random Int
Обратите внимание, что все экземпляры с одинаковыми value1
и value2
равны, даже если значение skew
отличается. В каждом объекте данных также есть лишний val skewCondition
, который кажется ненужным.
Этот ответ очень хорош, но на самом деле целью является абстрагирование перекоса, и реализующие классы все еще хорошо осведомлены о все еще существующем перекосе.
Если вы хотите скрыть детали реализации skew, вы можете использовать собственный тип scala.
trait SkewTrait {
def skew: Int
}
// package MyClass1
sealed class SkewClass1(value1: Int, value2: Int) {
this: SkewTrait =>
override def skew: Int = if (value1 == 500 && value2 == 700) 3 else 0
}
case class MyClass1(value1:Int, value2: Int) extends SkewClass1(value1, value2) with SkewTrait
// package MyClass2
sealed class SkewClass2(value1: Int) {
this: SkewTrait =>
override def skew: Int = if (value1 == 300) 4 else 0
}
case class MyClass2(value1: Int, value2: Int) extends SkewClass2(value1) with SkewTrait
println(MyClass1(1,2).skew)
println(MyClass2(300,5).skew)
результат:
0
4
Вы не можете создать case class
с полями, которые инициализируются из других полей, но вы можете использовать для этого объект класса. Начните с функции для вычисления перекоса:
def computeSkew(value1: Int, value2: Int) =
if (value1 == 500 && value2 == 700) 0 else Random.nextInt()
Затем используйте объект класса для создания его экземпляров:
case class MyClass1 private(value1: Int, value2: Int, skew: Int)
object MyClass1 {
def apply(value1: Int, value2: Int): MyClass1 =
MyClass1(value1, value2, computeSkew(value1, value2))
}
Другой вариант — добавить поле, расширив класс Skew
, но объекты с одинаковыми value1
и value2
всегда будут сравниваться как равные, потому что skew
не будет включен в тест equals
.
Как упоминалось в комментариях, я не уверен, что добавление skew
к основным объектам данных — это правильный способ решения проблемы дублирования. Было бы лучше обернуть skew вокруг объекта при использовании его в таблице и сохранить исходные данные нетронутыми.
Простая оболочка будет выглядеть так:
case class WithSkew[T] private(data: T, skew: Int)
object WithSkew {
def apply[T](data: T): WithSkew[T] =
WithSkew(data, Random.nextInt())
}
val x = MyClass1(500, 700)
val xSkew = WithSkew(x)
Затем вы используете xSkew
в таблице, чтобы избежать дублирования, и извлекаете поле data
, когда вы снова удаляете строки из таблицы.
Предыдущая версия не выполняет пользовательский расчет перекоса на основе value1
и value2
. Это можно решить, введя класс типов, чтобы дать расчет перекоса для определенного класса:
trait HasSkew[T] {
def skew(instance: T): Int
}
case class DefaultSkew[T]() extends HasSkew[T] {
def skew(instance: T): Int = Random.nextInt()
}
object HasSkew {
implicit object skew1 extends HasSkew[MyClass1] {
def skew(data: MyClass1): Int =
computeSkew(data.value1, data.value2)
}
}
case class WithSkew[T] private(data: T, skew: Int)
object WithSkew {
def apply[T](data: T)(implicit sk: HasSkew[T] = DefaultSkew[T]()): WithSkew[T] =
WithSkew(data, sk.skew(data))
}
Оболочка WithSkew
будет использовать расчет перекоса в DefaultSkew
, если только для этого класса не существует неявного экземпляра HasSkew
.
В этой версии больше стандартного кода, но она позволяет обернуть любой класс значением skew
и позволяет при необходимости адаптировать расчет перекоса к каждому конкретному классу.
Итак, по сути, в последнем ответе все, что нужно сделать классу, — это расширить WithSkew и, при необходимости, переопределить функцию перекоса, чтобы выполнить некоторые другие вычисления перекоса?
Нет, вы вообще не трогаете исходный класс. Вы используете WithSkew
для сворачивать исходного объекта, прежде чем поместить его в таблицу так же, как и вторую версию. В последней версии просто добавлена возможность создания собственного метода для вычисления перекоса для определенного класса.
это работает для вас со значением по умолчанию для параметра
skew
case class MyClass1(value1: Int, value2: Int, skew: Int = RandomInt)
?