1

我正在使用 java JDBC 编写一个应用程序,该应用程序查询数据并将其插入 Oracle 数据库。

我正在使用 springframework API 中的 SimpleDriverDataSource 来实现标准的 JDBC DataSource 接口。

这是我的代码的一部分

dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(Class.forName(credentials.getDriverClass()));

我试图保持代码独立于所使用的 DriverClass,并且我知道它class.forName()返回类字符串名称的类对象。

问题是我收到一个编译错误说:

setDriverClass(Class<? extends Driver>)类型中的方法SimpleDriverDataSource不适用于参数(Class<capture#1-of ?>

我真的不明白这些符号是什么意思,或者是什么导致了错误?

4

2 回答 2

4

SimpleDriverDataSource#setDriverClass(Class)实现为

public void setDriverClass(Class<? extends Driver> driverClass) {
    this.driver = BeanUtils.instantiateClass(driverClass);
}

所以它期待一个Class类型的对象,它是 的子类型Driver

Class.forName(String)方法实现为

public static Class<?> forName(String className)
            throws ClassNotFoundException {
    return forName0(className, true, ClassLoader.getCallerClassLoader());
}

换句话说,它返回一个Class<?>对象,即。任何类型的Class对象,不一定是 的子类型Driver。因此,返回对象的声明类型不是该setDriverClass()方法的有效参数。

一种解决方案是自己实例化您的Driver类并改用该setDriver(Driver)方法

Class<?> clazz = Class.forName(credentials.getDriverClass());
Object driver = BeanUtils.instantiateClass(clazz);
dataSource.setDriver((Driver) driver);

请注意,ClassCastException如果您尝试实例化的类不是Driver.

或者,正如 BalusC 所建议的,您可以转换返回的值Class.forName()

SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass((Class<Driver>)Class.forName("com.mysql.jdbc.Driver"));

@SuppressWarnings如果您不喜欢 IDE 警告,请添加一些。

于 2013-11-04T15:27:30.087 回答
1

这是一个值得了解的 Java 泛型小技巧。

当您处理已知一致但在代码中不明确的类型参数时,就会出现问题。如果您正在处理不完全类型化的集合,这很常见。

为了使事情更清楚,我将使用以下示例:考虑一个将各种值从一个地方传输到另一个地方的系统(也许它是一个调度程序在系统内发送不同类型的消息)。

我们可能有一个可以提供和接收某些消息类型的接口:

public interface Connection<Type>
{
  Type read();
  void write(Type value);
}

我们的调度器可能看起来像这样:

class Scheduler
{
  public void process(Collection<Connection<?>> cnxs)
  {
    for (Connection<?> cns: cnxs) {
      cnx.write(cnx.read);
    }
  }
}

(注意这是简写,我们在这里使用它是因为 cnxs 集合包含一个具有各种不同类型参数的 Connections)。

不幸的是,这不会编译!Eclipse with Java 1.6 给出的错误是“Connection 类型的方法 write(capture#2-of ?) 不适用于参数 (capture#3-of ?)”。

无法编译的原因是 Connection 返回的值的类型参数和它将接收的值的类型参数被分开处理。每个都被视为“捕获?” 这意味着“对象的某个子类”。然后编译器(可以理解)说“我不能将'对象的子类 X'发送到期望'对象的子类 Y'的方法,因为我不知道它们是否是同一个子类”。

为了完成这项工作,我们需要显式地引入公共类型参数。不幸的是,以下代码或类似代码不起作用(据我所知)。没有办法在代码块中间引入类型参数(我们这里真正想要的是更好地支持多态性):

class Scheduler
{
  public void process(Collection<Connection<?>> cnxs)
  {
    // syntax error!
    for (<E> Connection<E> cns: cnxs) {
      E value = cnx.read();
      cnx.write(value);
    }
  }
}

但是我们可以做的是添加一个引入新类型参数的辅助方法:

class Scheduler
{
  public void process(Collection<Connection<?>> cnxs)
  {
    for (Connection<?> cnx: cnxs) {
      helper(cnx);
    }
  }
  private <E> void helper(Connection<E> cnx)
  {
    E value = cnx.read();
    cnx.write(value);
  }
}

这就是我们想要的!代码验证、编译和运行。

总而言之:有时您可能会“丢失”显式泛型类型参数(通常是因为您正在处理不同类型的集合)。您可以通过添加额外的辅助方法来重新引入该类型参数。

于 2013-11-04T15:23:27.687 回答