泛型和ReadObject

Gor*_*onM 2 java generics serialization network-programming

我有一个使用泛型和对象序列化的简单服务器.(T是输入格式,U是输出格式).仅处理输入的简化版本如下所示:

public class Server <T, U> implements Runnable {

    @override
    public void run () {

    try (ObjectInputStream inReader = new ObjectInputStream (this.connection.getInputStream ())) {
        T   lastObj;
        while (true) {
            lastObj = (T) inReader.readObject ();
            System.out.println (lastObj.getClass ().getName ());
            if (null != lastObj) {
                this.acceptMessage (lastObj);
            }
        } catch (IOException | ClassNotFoundException ex) {
            Logger.getLogger (this.getClass ().getName ()).log (Level.SEVERE, ex.getMessage (), ex);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我启动服务器

Server <Integer, String> thisServer = new Server ();
Run Code Online (Sandbox Code Playgroud)

那么我希望它只接受Integer对象并返回Strings作为输出.

但是,我使用的是从System.in读取的简单客户端,用于测试和发送字符串到服务器.令我惊讶的是,服务器接受了输入.只是为了确保它真的接受了一个非TI类型的对象,添加了一行来回显出最后一个对象是什么类.

System.out.println (lastObj.getClass ().getName ());
Run Code Online (Sandbox Code Playgroud)

这确实输出了Java.lang.String.

这完全出乎意料.我认为Generics应该允许你传递类本身没有指定的类型的对象,而不必抛出对象?对T的演员似乎也没有效果.

这意味着理论上我可以通过提供它不期望的类型的Java对象来攻击服务器(或其消费者).虽然这不一定非常强大(因为它是一个学术项目,而不是生产软件),但我想知道你用readObject获得的对象不是你想要的那个,所以你可以处理它很重要.

我尝试添加以下内容,但它被标记为编译时错误.

if (lastObj instanceof T) {
}
Run Code Online (Sandbox Code Playgroud)

我该如何正确处理?

Pau*_*ora 5

正如其他人所指出的,这个问题与类型擦除有关.在运行时,T已被删除到其上限,Object.

当你强制转换时T,它被称为未经检查的强制转换,因为它在运行时不存在.相反,编译器在其中将实例T分配回到类似的实例类型的位置插入了其他强制类型转换Integer.当run消耗类似的意外类型时String,JVM无法区分它,并且它不会快速失败.如果有方法T getLastObject,则该方法的调用方可能会失败:

Server<Integer, String> thisServer = ...;
thisServer.run(); // consumes a String, but doesn't fail
Integer i = thisServer.getLastObject(); // ClassCastException thrown here
Run Code Online (Sandbox Code Playgroud)

解决方法是提供Server一个Class<T>表示要使用的对象类型的对象并使用该cast方法:

public class Server <T, U> implements Runnable {

   private final Class<T> readObjectType;

   public Server(final Class<T> readObjectType) {
       this.readObjectType = readObjectType;
   }

    @Override
    public void run () {

    try (ObjectInputStream inReader = new ObjectInputStream (this.connection.getInputStream ())) {
        T   lastObj;
        while (true) {
            lastObj = readObjectType.cast(inReader.readObject());
            System.out.println (lastObj.getClass ().getName ());
            if (null != lastObj) {
                this.acceptMessage (lastObj);
            }
        } catch (IOException | ClassNotFoundException ex) {
            Logger.getLogger (this.getClass ().getName ()).log (Level.SEVERE, ex.getMessage (), ex);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

readObjectType.cast(inReader.readObject()) 现在,当读取错误类型的对象时,它将快速失败.