我正在调用 api 来获取输入流,然后调用静态方法 parseFrom(inputstream) 将其转换为 protobuffclass。
如果我使用特定的类来完成它,它会起作用:
public CustomerDTOOuterClass.CustomerDTO GetCustomer()
{
CustomerDTOOuterClass.CustomerDTO customer = null;
try
{
URL url = new URL("https://localhost:44302/Api/customer/1?");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/x-protobuf");
conn.connect();
InputStream is = conn.getInputStream();
CustomerDTOOuterClass.CustomerDTO customer =
CustomerDTOOuterClass.CustomerDTO.parseFrom(is);
conn.disconnect();
}
catch(Exception ex)
{
System.out.println("[ "+ex.getMessage()+" ]");
}
return customer;
}
但是如果我将其更改为泛型类型,它会失败,因为 T 没有方法 parseFrom,是否有任何我可以在 T 中实现的接口,以便我可以调用 parseFrom 方法?
public T GetObject()
{
T object = null;
try
{
URL url = new URL("https://localhost:44302/Api/customer/1?");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/x-protobuf");
conn.connect();
InputStream is = conn.getInputStream();
T object = T.parseFrom(is);
conn.disconnect();
}
catch(Exception ex)
{
System.out.println("[ "+ex.getMessage()+" ]");
}
return object;
}
这是我得到的错误: 错误:(68, 27) 错误:找不到符号方法 parseFrom(InputStream)
每个生成的 protobuf 类型都包含一个名为
PARSER
的静态成员,它是 com.google.protobuf.Parser<T>
接口的实现。您的 getObject
方法只需要将 Parser<T>
作为参数即可。那么你可以这样称呼它:
Foo foo = getObject(Foo.PARSER);
如果你想对
T
执行此操作,将 Class<T>
(即 Proto 类型的类)传递到类的构造函数中,然后从中获取 Parser
会更容易、更自然,如下所示:
public class Thing<T extends Message> {
final Parser<T> parser;
Thing(Class<T> cls) {
parser = (Parser<T>) cls.getMethod("parser").invoke(null);
}
T deserialize(byte[] bytes) {
parser.parseFrom(bytes); // try/catch etc
}
}
扩展 Kenton Varda 的答案:
首先,我将您的方法重构为单独的方法来获取输入流并解析它。只有后者才有理由通用。
public InputStream getInputStream() {
// get it
}
现在您打算解析输入流并从 protobuf 构建 POJO。在我看来,此时您的代码必须知道您将获得什么类型的对象是合理的,因为否则接下来您将如何用它做一些智能的事情呢?例如
InputStream is = getInputStream();
Object o = parseGenericInputStream(is);
doSomethingWithParsedObject(o); // how to do this if you don't know o's type?
一旦解析了
o
(因此在解析它之前),你就必须知道它的类型,否则你无法用它做任何我能想到的有意义的事情。
所以...再次感谢 Kenton Varda:
public void doStuff() {
...
InputStream is = getInputStream();
MyProtobufClass pojo = parseGenericInputStream(MyProtobufClass.PARSER, is);
doSomethingWithParsedObject(pojo);
...
}
private <T> T parseGenericInputStream(Parser<T> parser, InputStream inputStream)
throws InvalidProtocolBufferException {
return parser.parseFrom(inputStream);
}
此时,尽管您正在为一行代码编写一个通用方法,但如果您问我,这有点不值得。
不,没有;如果不知道原型的类型,就无法反序列化原型。
如果您确实知道它的类型,那么您可以传入其类型的 Builder。
(此外,您不能在类型变量上调用静态方法,如
T
。)
两步:
@FunctionalInterface
public interface ParserFunction<T, R> {
R apply(T t) throws IOException;
}
public class Loader {
public static <T> T loadPbFile(
String path,
ParserFunction<FileInputStream, T> parser) throws IOException {
FileInputStream inputStream = getFileInputStream(path);
return parser.apply(inputStream);
}
}
现在只需使用 MyProtobufClass.Entity::parseFrom 作为泛型函数的第二个参数。