Можно ли в Scala создать экземпляр объекта универсального типа T?

В Scala, даже если решение не является элегантным, возможно ли создать/создать новый объект универсального типа T? Можно ли добиться этого с помощью отражения?

Например, меня интересует что-то вроде следующего:

    case class Person(name: String, age: Int)

Допустим, я хотел сделать следующее, чтобы создать объект типа Person:

    def createObject[T](fieldValues: Seq[Any]): T = {
        ... T(fieldValues)
    }

    val person = createObject[Person](Seq("Bob", 20))

person code    schedule 07.08.2020    source источник
comment
Откуда идут Seq[Any]? Предлагаю подумать о классе типов, а не о небезопасном отражении во время выполнения.   -  person cchantep    schedule 07.08.2020
comment
дублирование stackoverflow.com/questions/21715164/   -  person Artem Sokolov    schedule 07.08.2020
comment
Отвечает ли это на ваш вопрос? Как создать экземпляр типа T во время выполнения с TypeTags   -  person Artem Sokolov    schedule 07.08.2020
comment
Если T является классом case, вы можете сделать это даже во время компиляции (используя отражение во время компиляции, т.е. макросы под капотом) scastie.scala-lang.org/R7JABtX1RjizE7BsaPVqeQ   -  person Dmytro Mitin    schedule 07.08.2020


Ответы (2)


Технически это возможно с использованием отражения. Например, вы можете поймать класс времени выполнения типа T, используя ClassTag, затем найти правильный конструктор и создать экземпляр:

def createObject[T](fieldValues: Seq[Any])(implicit ct: ClassTag[T]): Option[T] = {
   //we lookup for matching constructor using arguments count, you might also consider checking types
   ct.runtimeClass.getConstructors.find(_.getParameterCount == fieldValues.size) 
   .flatMap {constructor =>
        Try(constructor.newInstance(fieldValues: _*).asInstanceOf[T]).toOption
    }
}

createObject[Person](Seq("Bob", 20)) //Some(Person("Bob", 20))
createObject[Person](Seq(20, 10)) //None

В случае отсутствия соответствующих параметров конструктора эта функция не возвращает None.

Это должно работать, но было бы лучше, если бы вы могли избежать этого подхода, потому что вы теряете безопасность всех типов.

person Krzysztof Atłasik    schedule 07.08.2020
comment
Спасибо @Krzysztof. Это было то, что я искал. Для меня это выглядит нормально, но, похоже, выдает ошибку: найдено несоответствие типов: Seq[Any] , требуется: Seq[Object] - person code; 08.08.2020
comment
О, я вижу, это потому, что ввод имеет тип Any, а конструктор из Java ожидает тип Object. - person code; 08.08.2020

Нет, это невозможно. T — это параметр. Вы не знаете ничего об этом. Вы даже не знаете, можно ли его создать вообще. Это может быть трейт, абстрактный класс, одноэлементный или составной тип.

В этом весь смысл параметрического полиморфизма. Для написания кода, которому не нужно ничего знать о типах, с которыми он имеет дело.

Например, совершенно законно вызывать ваш метод следующим образом:

val goodLuck = createObject[Nothing](Seq(1, 2))

Итак, Nothing буквально определяется как тип, у которого не может быть экземпляра. Как вы собираетесь реализовать это?

person Jörg W Mittag    schedule 07.08.2020
comment
Понятно, спасибо Йорг. Просто любопытно, если T расширяется из класса, который, как я знаю, имеет конструктор, возможно ли синтаксически создать экземпляр универсального типа T? - person code; 07.08.2020
comment
Вы можете использовать неявные доказательства для предоставления некоторой фабрики - если тип имеет неявный характер, вы можете использовать его для создания экземпляра. Но передача последовательности Any - это действительно плохой дизайн, где в лучшем случае вы можете вернуть Option[T], чтобы указать, что аргументы могут не соответствовать ожидаемому классу. - person Mateusz Kubuszok; 07.08.2020