【问题标题】:How to call a method returning generic type correctly (Java)?如何正确调用返回泛型类型的方法(Java)?
【发布时间】:2021-02-14 14:18:48
【问题描述】:

我有一个泛型方法要返回泛型类型。我想正确调用它以返回编译错误,以防我使用它的返回类型不符合边界,从而避免运行时错误(与编译相比)并使用 Java 泛型类型的所有功能。

public <T extends IDeserializable> T restoreObject(byte[] objectArray) {

    T object = T.getDeserialized(objectArray);
    
    return object;
}

但是,这样称呼它

File ret = restoreObject(new byte[] {1,2,3});

不会产生编译错误,因此错误只会在运行时产生。 File 类型没有实现 IDeserializable,但是 java 不会产生编译错误。

public interface IDeserializable {
        public static <T> T getDeserialized(byte[] data) {
            return null;
        }
}

【问题讨论】:

  • 第 2 行无法编译:T.getDeserialized(objectArray); 不是有效的 java。
  • 而 java 不知道这不会成功,因为您可能定义了一个类 class Something extends File implements IDeserializable,这将使分配工作。
  • 不,它不会编译,因为T 不作为类型或实例存在,除了强制转换为 T 之外,您不能对 T 做任何事情。如果它存在,它将是类型,而不是实现方法或符合任何东西的实例。
  • 谢谢。你说的对。它在界面中是静态的。更新了问题中的界面。
  • 但是现在你在 java 能做什么方面又一次不走运了。该接口中的静态方法不能被覆盖,并且将永远保留return null 或您在那里拥有的任何其他特定主体。但是类 A implements IDeserializableB implements IDeserializable 相比不能有不同的主体/逻辑,这使得该方法对您的用例完全无用。

标签: java generics


【解决方案1】:

这里有几个问题:

  • 在类型参数上调用静态方法并不像您想象的那样工作(here 是一个关于此的问题)。
  • 无论如何,getDeserialized是实例方法,不是静态方法,所以你不能做T.getDeserialized(objectArray)
  • 编译器无法像那样推断 T @luk2302 已纠正我 - T 将被推断为您分配结果的类型。

你需要给restoreObject一个T类型的参数,以便编译器可以推断T,这样你就可以调用getDeserialized方法:

public <T extends IDeserializable> T restoreObject(byte[] objectArray, T t) {
    return t.getDeserialized(objectArray);
}

但是,我怀疑您的意思是 getDeserialized 是一个静态方法。不幸的是,你不能这样做,因为静态方法不会被覆盖。但是,您可以使 restoreObject 采用 Function 参数:

public <T extends IDeserializable> T restoreObject(byte[] objectArray, Function<byte[], T> deserialize) {
    return deserialize.apply(objectArray);
}

然后您可以为 deserialize 参数使用 lambda 或方法引用。


您也可以使用Serializable,但@daniu 有pointed outSerializable 将在未来某个时间被删除,这不是一个好主意。那里已经有大量的库,因此您不必自己处理序列化/反序列化。 Here 是 Java 中序列化的一些替代方案。如果您不想使用外部库,Java 也有自己的 XML API。

【讨论】:

  • 正确,除了最后一点,编译器可以“推断”T,它就是你在赋值左侧写的类型。
  • 推荐使用Serializableit's been called a "horrible mistake" by Oracle itself 有点不合时宜,他们计划将其删除。尽可能避免。
  • @daniu 哦,我不知道,我不知道这些事情。我现在添加了一个关于替代品的问题的链接,这样更好吗?
【解决方案2】:

如果您可以想象一个理论类型Weirdo 可以做到,这会变得非常明显:

 class Weird extends File implements IDeserializable

然后:

static <T> T getDeserialized(byte[] data) {
        return new Weirdo();
}

甚至更简单(因为无法强制转换为类型参数):

 static <T> T getDeserialized(byte[] data) {
      return (T) "";
 }

您可以看到问题出在您的getDeserialized 开始,呼叫者必须信任您。但是如果你认为为什么接口中存在static方法(它们的主要目的是返回接口的一个instance),那么你就是在滥用这些方法。如果没有人可以覆盖它,那么将其设为static 有什么意义?您可能需要重新考虑您正在尝试做的事情。

但无论如何,如果你用 class 替换它 - 你得到一个编译时错误(不过我会让你弄清楚原因):

public static class IDeserializable {

    static <T> T getDeserialized(byte[] data) {
        return ...;
    }
} 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 2012-07-11
    • 2016-12-24
    • 1970-01-01
    相关资源
    最近更新 更多