泛型和ReadObject [英] Generics and ReadObject

查看:85
本文介绍了泛型和ReadObject的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a simple server that uses generics and object serialization. (T is the input format, U is the output format). A simplified version that only deals with input is shown below:

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);
        }
    }
}

如果我使用

Server <Integer, String> thisServer = new Server ();

然后我希望它仅接受Integer对象并返回Strings作为输出.

then I would expect it to only accept Integer objects and return Strings as output.

但是,我使用的是一个简单的客户端,该客户端从System.in中读取内容以进行测试并将字符串发送到服务器.令我惊讶的是,服务器接受了输入.为了确保它确实接受了不是T类型的对象,我添加了一行以回显最后一个对象是什么类.

However, I was using a simple client that read from System.in for testing and sending strings to the server. Much to my surprise, the server accepted the input. Just to make sure that it really was accepting an object that wasn't of type T I added the line to echo out what class the last object was.

System.out.println (lastObj.getClass ().getName ());

这实际上输出了Java.lang.String.

This did in fact output Java.lang.String.

这完全是意外的.我以为泛型应该允许您传递在类本身中未指定的类型的对象而不必强制转换对象?强制转换为T似乎也没有作用.

This is totally unexpected. I thought Generics were supposed to allow you to pass objects of a type that wasn't specified in the class itself without having to cast objects? The cast to T doesn't seem to have an effect either.

从理论上讲,这意味着我可以通过向服务器(或服务器的使用者)提供非预期类型的​​Java对象来攻击它.尽管这不必是超级健壮的(因为这是一个学术项目,而不是生产软件),但我认为知道readObject附带的对象不是您想要的对象,因此您可以处理它很重要.

This means in theory I could attack the server (or its consumer) by feeding it Java objects of a type it wasn't expecting. Whilst this doesn't have to be super-robust (because it's an academic project and not production software) I think knowing that the object that you got with readObject wasn't the one you wanted so you can deal with it is important.

我尝试添加以下内容,但只是将其标记为编译时错误.

I tried adding the following, but it just got flagged up as a compile time error.

if (lastObj instanceof T) {
}

我该如何正确处理?

推荐答案

正如其他人指出的那样,此问题与

As others have pointed out, this issue is related to type erasure. At runtime, T has been erased to its upper bound, Object.

当强制转换为T时,这称为 unchecked 强制转换,因为它在运行时不存在.取而代之的是,编译器在将T实例分配回诸如Integer之类的类型后再插入其他转换.当run使用诸如String之类的意外类型时,JVM无法分辨出区别,并且它不会很快失败.如果有方法T getLastObject,则该方法的调用者可能会失败:

When you cast to T, that's known as an unchecked cast because it doesn't exist at runtime. Instead, other casts have been inserted by the compiler in places where instances of T are assigned back to a reified type like Integer. When run consumes an unexpected type like String, the JVM can't tell the difference, and it doesn't fail fast. If there were a method T getLastObject, the caller of that method might fail instead:

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

解决方法是为Server提供 Class<T> 对象,代表要使用的对象的类型,并使用

The workaround is to provide Server with a Class<T> object representing the type of object to be consumed and use the cast method:

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);
        }
    }
}

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

readObjectType.cast(inReader.readObject()) will now fail fast when the wrong type of object has been read.

这篇关于泛型和ReadObject的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆