Java中标记接口的用途是什么?

62

当标记接口(如Serializable)中没有要实现的内容时,实现它的用途是什么?


6
http://en.wikipedia.org/wiki/Marker_interface_pattern - miku
10个回答

95

约书亚·布洛赫:《Effective Java》第二版,179页

条款37:使用标记接口定义类型

... 有人会说标记注解(第35条)使标记接口过时了。这种说法是不正确的。标记接口比标记注解有两个优点。首先,标记接口定义了被标记类的一种类型;而标记注解则没有。这种类型的存在可以让你在编译时捕获到错误,在运行时如果你使用标记注解则只能等到运行时才知道错误...

就我个人而言,我认为我会顺从约书亚在这个主题上更加出色的知识。


1
问题在于,这常常与第19条相矛盾:仅使用接口来定义类型。 "Serializable"不是一种类型,而是一种属性/行为。 - Tom B
6
实际上,即使"Serializable"可以被视为一种类型。考虑以下方法签名: public void serialize(Serializable objectToSerialize); 在这种情况下,在编译时就清楚地知道只有可序列化的对象可以传递给"serialize"方法,而这是注释所不能做到的。 - Mirko Klemm

67

在早期版本的Java中,标记接口是声明类元数据的唯一方式。例如,可序列化的标记接口允许类的作者表示他们的类在序列化和反序列化时会正常工作。

在现代Java中,标记接口已经没有用武之地了。它们可以完全被注解所取代,后者允许非常灵活的元数据功能。如果您有关于类的信息,并且该信息永远不会改变,则注解是表示它的非常有用的方式。


20
我非常喜欢注释,但在这种情况下,它们有一个缺点,即相对于使用 instanceof 的方式,检查它们会更加复杂。它们也没有在javadoc中很好地集成(在那里,一个快速的方法是查看实现<marker interface>或不实现的类列表)。因此,我认为它们仍然有用武之地,只是不像以前那样重要了。 - Fredrik
12
@Chris,那么你是正确的还是Josh Bloch在这里正确?https://dev59.com/xXI-5IYBdhLWcg3wJE0K#5080547 - Pacerier
9
想想 Hibernate 中的一个方法:Session.get(Serializable id)。通过使用标记接口,您可以指定标识符必须是可序列化的。如果我们改用注释,则在编译时无法再获得错误提示,例如 session.get(new Object())。相反,我们必须在运行时检查注释并抛出异常。 - Matt
2
顺便提一下,标记接口不能完全被注解所取代,因为你可以确保一个参数具有标记接口,但无法确保其具有注解,例如 writeObject(Serializable s)。这种对标记接口的检查无法用注解来替代。 - Peter Lawrey
2
根据这个逻辑(“没有行为的接口几乎不提供类型安全性”),Set 接口也不应该存在。它没有比 Collection 更多的方法,只是指定了契约。 - Malcolm
显示剩余14条评论

5

这表示该类(以及所有非瞬态字段)都可以进行序列化。如果您正在构建一个依赖于序列化的框架,您当然可以编写以下方法:

public void registerObject(Serializable obj);

限制您准备接受的类。

由于序列化对象需要在系统之间保持兼容性,因此序列化是一种明确的设计决策,因此需要使用标记接口来识别这些候选项。

还有一个安全方面。您不希望使所有内容都可序列化-否则,您可能会通过序列化意外地公开(例如)密码或其他敏感数据。


5
这样的标记接口在其他代码根据一个对象是否实现某个标记接口做出决策的情况下非常有用。
Serializable的情况下,将使用反射来序列化对象的字段。
现在更喜欢使用注解,因为它们不会传播到子类。
请参见标记接口模式

2
可序列化,而不是 ISerializable :) - Bozho
1
是的,我搞砸了,我总是在我的接口名称前加上"I" :) - Gregory Pakosz
3
@Gregory Heresy,这是Java!即使在.NET世界中,这种COM(不好的)约定也应该被遗忘! - Pascal Thivent
2
Eclipse不是Java代码的好例子,它可能是最糟糕的之一。 - TofuBeer
2
主观和争议性的 :p - Gregory Pakosz
显示剩余2条评论

5

它们被称为“标记”接口。正如其名称所示,它们标记某些对象可用于特定类型的操作。

Serializable表示该对象符合Java序列化规范。

有人讨论是否应该使用注解来取代它们,因为它们的功能非常相似。


3

如果您实现了一个接口,那么 instanceof 将会为真。如果您的接口没有任何需要实现的内容,那么可以使用它来为类打上元数据标记,就像Java 1.5及以上版本中的注释一样,而不必强制实现者进行任何特殊操作。


1

你的推理是正确的,一个空接口不会影响程序的“标准”执行,这个执行是基于字段的检查/变异和方法的分派。

然而,当与反射一起使用时,标记接口是有用的:一个库/方法通过反射检查一个对象,并且如果它的类实现了标记接口,则会以不同的方式工作。自Java5以来,很少需要标记接口 - 相同的“标记”功能可以通过Java注释实现 - 大多数效果都可以通过基于反射的代码实现。


0

我们通常会在需要检查类对象是否具有特定权限的场景中使用标记接口。我们使用 instanceOf 来检查权限。

public interface Herbivorous {
}
public interface Carnivorous {
}
public class Cow implements Herbivorous {
    String howMuchGrassConsumed() {
        return "2";
    };
}
public class Lion implements Carnivorous {
    String howManyCowsConsumed() {
        return "2";
    }
}
public class Jungle{
    public static void main(String[] args) {
        Cow cow = new Cow();
        Lion lion = new Lion();
        if(cow instanceof Herbivorous){
            System.out.println("Cow ate so much gress:"+cow.howMuchGrassConsumed());
        }else if(lion instanceof Carnivorous){
            System.out.println("Lion ate so many cows:"+lion.howManyCowsConsumed(););
        }else{
            System.out.println("That's an alien");
        }
    }
}

-2
仔细观察Java中的标记接口,例如Serializable、Clonnable和Remote,它们被用于向编译器或JVM指示某些内容。因此,如果JVM看到一个类是Serializable,它会对其执行一些特殊操作;同样地,如果JVM看到一个类实现了Clonnable,它会执行一些操作以支持克隆。RMI和Remote接口也是如此。简而言之,标记接口向编译器或JVM发出指示、信号或命令。
阅读更多:http://javarevisited.blogspot.com/2012/01/what-is-marker-interfaces-in-java-and.html#ixzz2v6fIh1rw

2
这是完全错误的。Java编译器或JVM对于Serializable和其他标记接口没有任何特殊处理,但使用它的Java库类需要识别Serializable对象,并且它们可能会以不同的方式考虑和处理这些对象,但不是编译器或JVM。这是关于标记接口的常见误解。 - Saurabh Patil

-2

主要目的是告诉编译器,对于实现了标记接口的类的对象进行不同的处理。


2
这是错的。Java编译器或JVM对Serializable和其他标记接口没有任何特殊处理,但是使用它的Java库类需要识别Serializable对象,并且它们可以考虑并以不同方式处理这些对象,但编译器或JVM不会做出这样的处理。这是关于标记接口的一个常见误解。 - Saurabh Patil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接