【问题标题】:How to instantiate a parameterized type class by name [duplicate]如何按名称实例化参数化类型类[重复]
【发布时间】:2018-09-14 14:55:05
【问题描述】:

以下示例:

我有一项可以“启动”任何车辆的服务

interface VehicleStarterService<T: Vehicle> {
   fun <T : Vehicle> start(vehicle: Class<T>): String {
       return vehicle.start
   }
}

我想按名称启动一个车辆类型,例如“Car”,这需要我创建一个 VehicleStarterService;但是我似乎无法找到一种方法来用按名称实例化的类来实例化接口。

我想做一些类似(但不能)的事情:

val cls = "Car"
val kClass = Class.forName(cls).kotlin
val service = VehicleStarterService<kClass>()
service.start

我最终不得不执行以下操作(为我需要的每个参数化类型创建一个服务):

class CarStarterService : VehicleStarterService<Car> {
    fun <T> execute() : String {
        return start(User::class.java)
    }
}

有没有办法以这种方式实例化参数化类?

【问题讨论】:

  • 我会说不。类型实例化必须在编译时发生,但字符串中的类型名称仅在运行时才知道。根据start 真正应该做什么,也许泛型不是必需的,所有车辆的通用接口加上特定车辆的类对象就足够了。
  • 你要做的就相当于一个raw类型,所以就用raw类型吧。

标签: java generics kotlin parameterization


【解决方案1】:

尚不清楚这是否足以满足您的情况,但也许您可以像这样根据字符串匹配类

val cls = "Car"
val service: VehicleStarterService<out Vehicle>? = when (cls) {
    "Car"  -> object : VehicleStarterService<Car> {}
    "Boat" -> object : VehicleStarterService<Boat> {}
    "Plane" -> object : VehicleStarterService<Plane> {}
    else   -> null
}
service?.start(...

编辑:hashmap 注册表的想法,允许一些可扩展性..

val serviceRegistry = HashMap<String, VehicleStarterService<out Vehicle>>()
    .apply {
        //default services
        this["Car"] = object : VehicleStarterService<Car> {}
        this["Boat"] = object: VehicleStarterService<Boat> {}
    }
.
.
.
//Adding entry
serviceRegistry["Plane"] = object: VehicleStarterService<Plane> {}
.
.
.
//Fetching entry
val cls = "Car"
val service = serviceRegistry[cls]

service?.start(...

【讨论】:

  • 我希望能够避免添加这样的匹配器(要求其他人更改底层的“库”类)但是是的,这绝对是一种选择!
  • 我在第二个想法中进行了编辑,使用 hashmap 作为注册表,以便其他人可以添加自己的服务并获取这些服务
【解决方案2】:

我真的没有看到你的问题。请注意,泛型类型信息在运行时被删除(至少对于您显然正在使用的 JVM 变体)。因此,您使用哪种通用类型的服务基本上并不重要。 您也可以只使用VehicleStarterService&lt;Vehicle&gt;,它会启动您的所有车辆。如果出于任何原因您需要调用特定(其他!)函数,您仍然需要检查/转换为您的实际类型......所以我认为尝试获得该通用类型接口没有任何好处。

话虽如此,仍然有方法可以做到...(再次注意:这并不重要...泛型类型信息在运行时被删除...)

现在我的测试使用这个入口方法:

fun startAllTheVehicles(vararg vehicleTypes: String) {
  startAllTheVehicles(*vehicleTypes.map { Class.forName(it) }
                                   .map { it as Class<out Vehicle> }
                                   .toTypedArray())
}

请注意,it as Class&lt;out Vehicle&gt; 并不能确保您拥有 Vehicle 类型的类。它只是确保您的代码在尝试调用将 Class&lt;out Vehicle&gt; 声明为参数类型的函数时编译。

因此,从那里开始,您可以使用以下方法之一来获得实际的泛型类型。我假设您在启动服务中使用 newInstance 之类的东西。

  1. 接口上省略了泛型类型的示例(这种情况相当简单):

    interface VehicleStarterService {
      fun <T: Vehicle> start(vehicleType: Class<T>) = vehicleType.newInstance().start
    }
    

    上述入口函数使用一个服务启动所有车辆的示例方法:

    fun <T : Vehicle> startAllTheVehicles(vararg vehicleTypes: Class<out T>) {
      val service = object : VehicleStarterService {}
      vehicleTypes.forEach { service.start(it) }
    }
    
  2. 仍然使用泛型类型的接口(注意关于out T等的变化):

    interface VehicleStarterService<T: Vehicle> {
      fun start(vehicleType: Class<out T>) = vehicleType.newInstance().start
    }
    

    入口函数调用的示例方法,使用一个服务启动所有车辆:

    fun <T : Vehicle> startAllTheVehicles(vararg vehicleTypes: Class<out T>) {
      // actually it doesn't really matter which type we use...
      val service = object : VehicleStarterService<Vehicle> {} // you could even use T instead of Vehicle
      vehicleTypes.forEach { service.start(it) }
    }
    

使用以下测试都按预期工作:

startAllTheVehicles("Car", "Truck")

使用实际上不是Vehicle 的类型调用它会在接口的start 函数处为您提供ClassCastException

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多