Как отправить и получить типизированный объект в списке - PullRequest
0 голосов
/ 30 мая 2019

Я пытаюсь создать класс, в котором у меня есть две функции переопределения через интерфейс.Интерфейс будет типизирован, и я хочу, чтобы функции были просто преобразованиями.

Я смог выполнить эту работу, однако, когда я создал несколько этих объектов, поместил их в список и получил доступ к ним по отдельности, тогда функция, которая должна принимать универсальный тип в качестве входных данных, выдает ошибку:Type mismatch: inferred type is Any but Nothing was expected

Другими словами:

interface ConverterInterface<T>{
    fun sender(value: T): ByteArray
    fun receiver(bytes: ByteArray): T
}

И это работает, когда впервые реализовано:

class TestClass {
    val objA = object: ConverterInterface<String>{
        override fun sender(value: String): ByteArray {
            //...
        }

        override fun receiver(bytes: ByteArray): String {
            //...
        }
    }
}

Однако я хочу сохранить список этих объектови получить к ним доступ позже.

fun test(){
    val testList: List<ConverterInterface<*>> = listOf(objA)

    // This works
    testList[0].receiver(byteArrayOf())

    // This does not
    testList[0].sender("")
    /* Out-projected type 'ConverterInterface<*>' prohibits the use of
    'public abstract fun sender(value: T): ByteArray defined in ConverterInterface */
}

Мне пришлось использовать подстановочный знак * для типа списка, потому что, если бы я попытался использовать Any, я получил бы ошибку вывода типа.Чтобы заставить это работать, я мог бы установить тип как out Any, но это ничего не исправляет и заставляет работать только функцию receiver.

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

Ответы [ 2 ]

1 голос
/ 30 мая 2019

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

У вас есть List преобразователей,но вы не можете сказать, что каждый может преобразовать;каждый из них реализует ConverterInterface<T> для некоторого типа T, но вы не знаете, что такое T для какого-либо конкретного элемента в списке.

testList[0] (при условиив списке есть хотя бы один элемент) может реализовываться ConverterInterface<String>, или ConverterInterface<Int>, или ConverterInterface<Map<URL, LinkedList<LinearGradientPaint>>, или что-либо еще.Так что testList[0].realSender("") небезопасно, потому что он может не принимать String.

Вот почему компилятор жалуется;он ничего не знает о соответствующем типе, поэтому должен принять наихудший случай и запретить вызов.В противном случае вы рискуете ClassCastException во время выполнения, когда преобразователь не сможет обработать тип передаваемого вами параметра.

Еще хуже, из-за стирания типа вы не сможете найти этот тип ввремя выполнения, либо.(Во время выполнения, после того, как компилятор сделал свое волшебство, есть только простой ConverterInterface.)

Кстати, ответ с realSender() не решает эту проблему, просто сообщая компиляторузаткнись об этом.Он все еще будет кусать вас во время выполнения.

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

Почему вы храните эти конвертеры вlist, и зачем вам доступ к ним таким образом?

Если вы знаете, что каждая реализация сможет принимать строки, тогда сделайте список List<ConverterInterface<String>>: компилятор будет гарантировать, что все в спискеможет принимать строки, и вы можете безопасно передать строку любому конвертеру в списке.

Но если список может содержать конвертеры различных типов, то как вы узнаете, что конвертер, на который вы смотрите, можетвзять какой-то конкретный тип?Даже если код работает на данный момент, есть риск, что дополнительный элемент будет добавлен в список позже, и все испортится.Таким образом, вам придется отслеживать другой путь.И этот другой способ, вероятно, даст вам лучший (безопасный) способ доступа к ним.

0 голосов
/ 30 мая 2019

Я не уверен, что это даже хорошее решение, но вот что я придумала.Я создал абстрактный класс, который реализует интерфейс, но также содержит предопределенную функцию для отправителя, которая выполняет внутреннее преобразование T:

interface ConverterInterface<T>{
    fun sender(value: T): ByteArray
    fun receiver(bytes: ByteArray): T
}

abstract class TestClass<T> : ConverterInterface<T>{
    fun realSender(value: Any): ByteArray{
        @Suppress("UNCHECKED_CAST")
        return sender(value as T)
    }
}

val testObj = object: TestClass<String>(){
    override fun sender(value: String): ByteArray {
        return byteArrayOf()
    }
    override fun receiver(bytes: ByteArray): String {
        return ""
    }

}

val testList: List<TestClass<*>> = listOf(testObj)

fun a(){
    testList[0].realSender("")
    testList[0].receiver(byteArrayOf())
}

Я понимаю, что функция теперь принимает все, что помещено в нее, ичто это может вызвать исключения, но на данный момент это все, что я могу придумать в качестве решения.Кроме того, по крайней мере, в моем случае функции класса никогда не должны получать ничего, кроме их родового, поэтому передаваемое значение другого типа действительно будет исключением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...