泛型和ReadObject [英] Generics and 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屋!