На мой взгляд, это не семантика значений и ссылочная семантика, а идентичность.
Класс данных представляет значение. Значение является неизменным и не имеет идентичности, что означает, что два значения одинаковы тогда и только тогда, когда они имеют одинаковое содержимое. Вот почему компилятор может автоматически генерировать равенства и хэш-код.
Если вы хотите смоделировать сущность, которая имеет идентичность, то есть стабильную логическую сущность, связанную с серией различных значений во времени, вы должны использовать обычный класс и определить, какова его идентичность. Это может быть uuid или адрес памяти.
Также возможно смоделировать сущность, используя класс данных. Вы можете определить это так:
data class Plus(val id: Long, val a: Term, val b: Term) : Term()
Или больше похоже на предложение @marstran
data class Entity(val id: Long, val value: Any)
Решения @ marstran и @gidds практичны и выполняют свою работу, но я думаю, вы должны осторожно, чтобы не запутать ваш код. В IdentityHashMap есть даже это предупреждение javado c: Этот класс не является универсальной реализацией Map! Хотя этот класс реализует интерфейс Map, он намеренно нарушает общий контракт Map, который требует использования метода equals при сравнении объектов. Этот класс разработан для использования только в редких случаях, когда требуется семантика равенства ссылок.