перегрузка + и + = операторов для «классов номеров»

Я хочу создать функции расширения для классов, которые инкапсулируют простые Number s. Например, DoubleProperty . Я столкнулся с проблемой, что я не могу одновременно перегрузить оператор + и += .

Я не хочу создавать поведение, которое проходит следующие тесты:

 class DoublePropertyTest { lateinit var doubleProperty: DoubleProperty @Before fun initialize() { doubleProperty = SimpleDoubleProperty(0.1) } @Test fun plus() { val someProperty = doubleProperty + 1.5 assertEquals(someProperty.value, 1.6, 0.001) } @Test fun plusAssign() { val someProperty = doubleProperty doubleProperty += 1.5 //error if + and += are overloaded assert(someProperty === doubleProperty) //fails with only + overloaded assertEquals(doubleProperty.value, 1.6, 0.001) } } 

Он может быть реализован с использованием этих функций расширения:

 operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty = SimpleDoubleProperty(get() + number.toDouble()) operator fun WritableDoubleValue.plusAssign(number: Number) = set(get() + number.toDouble()) 

Проблема в том, что если + наложено += нельзя также перегрузить:

 Assignment operators ambiguity. All these functions match. - public operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty - public operator fun WritableDoubleValue.plusAssign(number: Number): Unit 

Если я перегружаю только оператор + , новый объект DoubleProperty возвращается на += операции вместо исходного.

Есть ли способ обойти это ограничение?

Странный += оператор в Котлине

вы можете как перегрузить оператор plus оператор plusAssign в kotlin, но вы должны следовать правилам kotlin для решения странных конфликтов += .

  1. ввести неизменную структуру класса для оператора plus что означает, что любой класс вне класса не может редактировать свои внутренние данные.

  2. ввести изменчивую структуру класса для оператора plusAssign что означает, что его внутренние данные могут редактироваться в любом месте.

kotlin уже сделал такие вещи в stdlib для классов Collection & the Map , Collection # plus и MutableCollection # plusAssign, как показано ниже:

 operator fun <T> Collection<T>.plus(elements: Iterable<T>): List<T> // ^--- immutable structure operator fun <T> MutableCollection<in T>.plusAssign(elements: Iterable<T>) // ^--- mutable structure 

Но подождите, как решить конфликт, когда мы используем оператор += ?

ЕСЛИ список является неизменяемой Collection тогда вы должны определить переменную переменной var , тогда используется оператор plus поскольку его внутреннее состояние не может быть отредактировано. например:

 // v--- define `list` with the immutable structure explicitly var list: List<Int> = arrayListOf(1); //TODO: try change `var` to `val` val addend = arrayListOf(2); val snapshot = list; list += addend; // ^--- list = list.plus(addend); // list = [1, 2], snapshot=[1], addend = [2] 

Если список является изменяемым MutableCollection тогда вы должны определить неизменяемую переменную val , тогда plusAssign оператор plusAssign поскольку его внутреннее состояние можно редактировать где угодно. например:

 // v--- `list` uses the mutable structure implicitly val list = arrayListOf(1); //TODO: try change `val` to `var` val addend = arrayListOf(2); val snapshot = list; list += addend; // ^--- list.plusAssign(addend); // list = [1, 2], snapshot=[1, 2], addend = [2] 

С другой стороны, вы можете перегружать оператора с помощью сигнатур diff, каждой подписи для разных контекстов , и kotlin также делает это, например: Collection # plus . например:

 var list = listOf<Int>(); list += 1; //list = [1]; // ^--- list = list.plus(Integer); list += [2,3]; //list = [1, 2, 3] // ^--- list = list.plus(Iterable); 

Реализация вашего оператора переопределения имеет две проблемы:

1. непоследовательный тип после plus

 operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty = SimpleDoubleProperty(get() + number.toDouble()) 

Любой экземпляр ObservableDoubleValue плюс Number , получил экземпляр DoubleProperty (или сказать экземпляр SimpleDoubleProperty ). Предположим, что у меня есть тип ComplexDoubleProperty реализующий ObservableDoubleValue , вы увидите:

 var a = getComplexDoubleProperty() a = a + 0.1 //compile error, WTF? //or even var b = SimpleDoubleProperty(0.1) b = b + 0.1 //compile error, because b+0.1 is DoubleProperty 

Вы можете видеть, что это поведение бессмысленно.

2. a = a + b и a + = b должны быть одинаковыми

Если ваша реализация компилируется, у вас будет

 var a: DoubleProperty = SimpleDoubleProperty(0.1) //add DoubleProperty to make it compile var b = a a += 0.1 println(b == a) 

prints true потому что += устанавливает значение в исходный экземпляр. Если вы замените a+=0.1 на a=a+0.1 вы получите false потому что возвращается новый экземпляр. Вообще говоря, a=a+b и a+=b не идентичны в этой реализации.

Чтобы исправить две проблемы выше, мое предложение

 operator fun SimpleDoubleProperty.plus(number: Number): SimpleDoubleProperty = SimpleDoubleProperty(get() + number.toDouble()) 

поэтому вам не нужно переопределять plusAssign . Решение не такое общее, как ваше, но это правильно, если у вас есть SimpleDoubleProperty вычисления SimpleDoubleProperty , и я полагаю, что вы это делаете, потому что в вашей реализации plus всегда возвращает экземпляр SimpleDoubleProperty .

Вы не можете перегружать как + и += . Перегрузите один из них.

Когда вы пишете + = в своем коде, теоретически могут быть вызваны как плюс функции plusAssign (см. Рис. 7.2). Если это так, и обе функции определены и применимы, компилятор сообщает об ошибке.

Я скопировал / вложил из Котлина в книгу действий !

Если DoubleProperty является вашим классом, вы можете сделать plus и plusAssign его методы, которые должны устранить любую двусмысленность.

  • Kotlin - Как я могу получить доступ к моей новой функции расширения класса из другого файла
  • Настройка привязок Guice в Котлине
  • Kotlin: Можете ли вы использовать именованные аргументы для varargs?
  • Что означает «. ()» В Котлине?
  • Могу ли я иметь другой тип возврата в котлин?
  • Как создать пакетные функции?
  • Есть ли способ показать все функции расширения данного класса Kotlin в Intellij IDE?
  • Функция расширения Kotlin
  • Как получить доступ к членам класса с таким же именем в функции расширения в Kotlin android
  • Kotlin: получить ссылку на функцию экземпляра класса
  • Превознесение Клейсли в Котлине
  • Давайте будем гением компьютера.