Почему не выводится значение поле класса в цикле for в kotlin?

Рейтинг: 0Ответов: 1Опубликовано: 21.04.2023

Имеется 3 демонстративных класса, два класса с "продуктом", и один класс для "заказа"

data class Coffee(
    val id:Int,
    val name:String,
    val price:Int
)

 data class Dessert(
    val id:Int,
    val name:String,
    val price:Int
)

data class Order(
    val id:Int,
    val orderItem:MutableList<Any>,
)

Потом я создаю экземпляры классов "продукта", и передаю их в класс "заказа"

val coffee=Coffee(1,"test_coffee",2)
val dessert=Dessert(1,"test_dessert",4)
val order=Order(3, mutableListOf(coffee, dessert))

После чего я хочу через цикл пройтись по orderItem заказа, и сложить цену всех продуктов которые там хранятся. Но даже при простой попытки вывести ценник возникает ошибка.

for(i in order.orderItem){
    println(i.price)
}
Unresolved reference: price

При условии того что тип данных внутри цикла определяется правильно.

for(i in order.orderItem){
    println(i::class.java.typeName)
}
FileKt$main$Coffee
FileKt$main$Dessert

Ответы

▲ 1Принят

Ну потому что у orderItem тип указан MutableList<Any>, значит для kotlin у переменной i будет тип Any, для которого не задано поле price.

Проще всего задать для всех заказов общий родительский класс или интерфейс, в котором указать поля, которые гарантированно есть у всех заказов, указать этот класс или интерфейс для orderItem вместо Any.

Через интерфейс:

interface OrderItem {
    val id: Int
    val name: String
    val price: Int
}

data class Coffee(
        override val id: Int,
        override val name: String,
        override val price: Int
) : OrderItem

data class Dessert(
        override val id: Int,
        override val name: String,
        override val price: Int
) : OrderItem

data class Order(
        val id: Int,
        val orderItem: MutableList<OrderItem>,
)

val coffee = Coffee(1, "test_coffee", 2)
val dessert = Dessert(1, "test_dessert", 4)
val order = Order(3, mutableListOf(coffee, dessert))

for (i in order.orderItem) {
    println("${i.name}: ${i.price}")
}

Или класс:

abstract class OrderItem(
        open val id: Int,
        open val name: String,
        open val price: Int
)

data class Coffee(
        override val id: Int,
        override val name: String,
        override val price: Int
) : OrderItem(id, name, price)

data class Dessert(
        override val id: Int,
        override val name: String,
        override val price: Int
) : OrderItem(id, name, price)

data class Order(
        val id: Int,
        val orderItem: MutableList<OrderItem>,
)

val coffee = Coffee(1, "test_coffee", 2)
val dessert = Dessert(1, "test_dessert", 4)
val order = Order(3, mutableListOf(coffee, dessert))

for (i in order.orderItem) {
    println("${i.name}: ${i.price}")
}

Вывод:

test_coffee: 2
test_dessert: 4

Еще вариант - явно проверять, что объект принадлежит конкретному классу, но это не удобно, если классов много, и не имеет особого смысла, если классы сильно пересекаются (много общих полей - в этом случае проще отнаследоваться от общего класса/реализовать общий интерфейс). Даже в данном случае с всего двумя классами появляется дублирующийся код:

for (i in order.orderItem) {
    if (i is Coffee) {
        println("${i.name}: ${i.price}")
    } else if (i is Dessert) {
        println("${i.name}: ${i.price}")
    }
    // или
    when (i) {
        is Coffee -> println("${i.name}: ${i.price}")
        is Dessert -> println("${i.name}: ${i.price}")
    }
}