Учитывая следующий код:
public static void main(String[] args) {
record Foo(int[] ints){}
var ints = new int[]{1, 2};
var foo = new Foo(ints);
System.out.println(foo); // Foo[ints=[I@6433a2]
System.out.println(new Foo(new int[]{1,2}).equals(new Foo(new int[]{1,2}))); // false
System.out.println(new Foo(ints).equals(new Foo(ints))); //true
System.out.println(foo.equals(foo)); // true
}
Кажется, очевидно, этот массив toString
, equals
используются методы (вместо статических методов Arrays::equals
, Arrays::deepEquals
или Array::toString
).
Поэтому я предполагаю, что записи Java 14 ( JEP 359 ) не очень хорошо работают с массивами, соответствующие методы должны генерироваться с помощью IDE (которая, по крайней мере в IntelliJ, по умолчанию генерирует «полезные» методы, то есть они используют статические методы). в Arrays
)
Или есть другое решение?
java
arrays
java-14
java-record
user140547
источник
источник
List
вместо массива?toString()
,equals()
иhashCode()
способы записи реализованы с использованием invokedynamic ссылки. , Если бы только эквивалент скомпилированного класса мог быть ближе к тому, что методArrays.deepToString
делает сегодня в своем закрытом перегруженном методе, он мог бы решить проблему для примитивов.Object
, поскольку это может произойти и с пользовательскими классами. например, неверно равноinvokedynamic
имеет абсолютно никакого отношения к выбору семантики; Здесь indy - чистая деталь реализации. Компилятор мог выдать байт-код, чтобы сделать то же самое; это был просто более эффективный и гибкий способ добраться туда. Во время разработки записей широко обсуждалось, следует ли использовать более тонкую семантику равенства (например, глубокое равенство для массивов), но оказалось, что это вызывает гораздо больше проблем, чем предполагалось.Ответы:
Массивы Java создают несколько проблем для записей, и это добавляет ряд ограничений к дизайну. Массивы являются изменяемыми, и их семантика равенства (наследуемая от Object) является идентичностью, а не содержимым.
Основная проблема в вашем примере состоит в том, что вы хотите, чтобы
equals()
в массивах подразумевалось равенство содержимого, а не ссылочное равенство. Семантика (по умолчанию)equals()
для записей основана на равенстве компонентов; в вашем примере двеFoo
записи, содержащие разные массивы , различаются, и запись ведет себя правильно. Проблема в том, что вы просто хотите, чтобы сравнение равенства было другим.Тем не менее, вы можете объявить запись с нужной вам семантикой, это просто займет больше работы, и вы можете почувствовать, что это слишком много работы. Вот запись, которая делает то, что вы хотите:
Это делает защитную копию на входе (в конструкторе) и на выходе (в методе доступа), а также корректирует семантику равенства для использования содержимого массива. Это поддерживает инвариант, требуемый в суперклассе
java.lang.Record
, который «разбивая запись на ее компоненты и восстанавливая компоненты в новую запись, дает равную запись».Вы вполне можете сказать: «Но это слишком много работы, я хотел использовать записи, чтобы мне не приходилось печатать все эти вещи». Но записи не являются в первую очередь синтаксическим инструментом (хотя они синтаксически более приятны), они являются семантическим инструментом: записи являются именными кортежами . В большинстве случаев компактный синтаксис также дает желаемую семантику, но если вам нужна другая семантика, вам придется проделать дополнительную работу.
источник
List< Integer >
обходной путьРешение: Используйте
List
изInteger
объектов (List< Integer >
) , а не массив примитивов (int[]
).В этом примере я создаю экземпляр неизменяемого списка неопределенного класса, используя
List.of
функцию, добавленную в Java 9. Вы также можете использоватьArrayList
изменяемый список, поддерживаемый массивом.источник
Обходной путь: создайте
IntArray
класс и обернитеint[]
.Не идеально, потому что теперь вам нужно звонить
foo.getInts()
вместоfoo.ints()
, но все остальное работает так, как вы хотите.Вывод
источник
record
может состоять из множества полей, и только поля массива оборачиваются так.List
которые предоставляют такую оболочку, которую можно искать с решением, предложенным здесь. Или вы считаете, что это может привести к накладным расходам для таких случаев использования?int[]
, то aList<Integer>
не то же самое, по крайней мере по двум причинам: 1) список использует намного больше памяти, и 2) между ними нет встроенного преобразования.Integer[]
🡘List<Integer>
довольно легко (toArray(...)
иArrays.asList(...)
), ноint[]
🡘List<Integer>
требует больше работы, а преобразование занимает время, поэтому вы не хотите делать это все время. Если у вас естьint[]
и вы хотите сохранить его в записи (с другими вещами, в противном случае зачем использовать запись), и вам нужно это какint[]
, тогда преобразование каждый раз, когда вам нужно, неправильно.Arrays.equals
, поArrays.toString
сравнению сList
реализацией, используемой при замене,int[]
как предложено в другом ответе. (Предполагая, что оба они в любом случае являются обходными путями.)