Marker Interfaces in Java – Java中的标记器接口

最后修改: 2019年 2月 7日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this quick tutorial, we’ll learn about marker interfaces in Java.

在这个快速教程中,我们将学习Java中的标记接口。

2. Marker Interfaces

2.标志物接口

A marker interface is an interface that doesn’t have any methods or constants inside it. It provides run-time type information about objects, so the compiler and JVM have additional information about the object.

标记接口是一个接口其内部没有任何方法或常量。它提供了关于对象的运行时类型信息,因此编译器和JVM拥有关于对象的额外信息

A marker interface is also called a tagging interface.

标记接口也被称为标记接口。

Though marker interfaces are still in use, they very likely point to a code smell, and we should use them carefully. The main reason for this is that they blur the lines of what an interface represents, since markers don’t define any behavior. Newer development favors annotations to solve some of the same problems.

尽管标记接口仍在使用,但它们非常有可能指向代码的味道,我们应该谨慎使用它们。主要原因是它们模糊了接口代表的界限,因为标记并没有定义任何行为。较新的开发倾向于使用注解来解决一些相同的问题。

3. JDK Marker Interfaces

3.JDK标记接口

Java has many built-in marker interfaces, such as Serializable, Cloneable, and Remote.

Java有许多内置的标记接口,如SerializableCloneableRemote.

Let’s take the example of the Cloneable interface. If we try to clone an object that doesn’t implement this interface, the JVM throws a CloneNotSupportedException. Thus, the Cloneable marker interface is an indicator to the JVM that we can call the Object.clone() method.

让我们以Cloneable接口>为例。如果我们试图克隆一个没有实现该接口的对象,JVM会抛出一个CloneNotSupportedException。因此,Cloneable标记接口是JVM的一个指标,我们可以调用Object.clone()方法。

In the same way, when calling the ObjectOutputStream.writeObject() method, the JVM checks if the object implements the Serializable marker interface. When this isn’t the case, a NotSerializableException is thrown. Therefore, the object isn’t serialized to the output stream.

同样,在调用ObjectOutputStream.writeObject()方法时,JVM会检查该对象是否实现了Serializablemarker接口。如果不是这样的话,就会抛出一个NotSerializableException。因此,该对象不会被序列化到输出流中。

4. Custom Marker Interface

4.自定义标记界面

Let’s create our own marker interface.

让我们创建我们自己的标记界面。

For example, we can create a marker that indicates whether an object can be removed from the database:

例如,我们可以创建一个标记,表明一个对象是否可以从数据库中删除。

public interface Deletable {
}

In order to delete an entity from the database, the object representing this entity has to implement our Deletable marker interface:

为了从数据库中删除一个实体,代表这个实体的对象必须实现我们的Deletablemarker接口。

public class Entity implements Deletable {
    // implementation details
}

Let’s say that we have a DAO object with a method for removing entities from the database. We can write our delete() method so that only objects implementing our marker interface can be deleted:

假设我们有一个DAO对象,有一个从数据库中删除实体的方法。我们可以编写我们的delete()方法,以便只有实现我们的标记接口的对象可以被删除。

public class ShapeDao {

    // other dao methods

    public boolean delete(Object object) {
        if (!(object instanceof Deletable)) {
            return false;
        }

        // delete implementation details
        
        return true;
    }
}

As we can see, we’re giving an indication to the JVM about the runtime behavior of our objects. If the object implements our marker interface, it can be deleted from the database.

正如我们所看到的,我们正在给JVM一个关于我们对象的运行时行为的指示。如果对象实现了我们的标记接口,它可以从数据库中删除。

5. Marker Interfaces vs. Annotations

5.标记接口与注解

By introducing annotations, Java provided us with an alternative to achieve the same results as the marker interfaces. Moreover, like marker interfaces, we can apply annotations to any class, and we can use them as indicators to perform certain actions.

通过引入注解,Java为我们提供了一个替代方案,以实现与标记接口相同的结果。此外,像标记接口一样,我们可以将注解应用于任何类,我们可以将它们作为执行某些动作的指标。

So what’s the key difference?

那么,关键的区别是什么?

Unlike annotations, interfaces allow us to take advantage of polymorphism. As a result, we can add additional restrictions to the marker interface.

与注解不同,接口允许我们利用多态性的优势。因此,我们可以为标记接口添加额外的限制。

For instance, let’s add a restriction that only a Shape type can be removed from the database:

例如,让我们添加一个限制,即只有一个形状类型可以从数据库中删除。

public interface Shape {
    double getArea();
    double getCircumference();
}

In this case, our marker interface, DeletableShape, will look like this:

在这种情况下,我们的标记界面,DeletableShape,将看起来像这样。

public interface DeletableShape extends Shape {
}

Then our class will implement the marker interface:

然后,我们的类将实现标记器接口。

public class Rectangle implements DeletableShape {
    // implementation details
}

Therefore, all DeletableShape implementations are also Shape implementations. Obviously, we can’t do that using annotations.

因此,所有的DeletableShape实现也是Shape实现。很明显,我们不能用注解来做这件事

However, every design decision has trade-offs, and we can use polymorphism as a counter-argument against marker interfaces. In our example, every class extending Rectangle will automatically implement DeletableShape.

然而,每一个设计决策都是有取舍的,我们可以用多态性作为反对标记接口的反驳理由在我们的例子中,每个扩展Rectangle的类都将自动实现DeletableShape.

6. Marker Interfaces vs. Typical Interfaces

6.标识器接口与典型接口

In the previous example, we could get the same results by modifying our DAO’s delete() method to test whether our object is a Shape or not, instead of testing whether it’s a Deletable:

在前面的例子中,我们可以通过修改DAO的delete()方法来测试我们的对象是否是Shape或不是而不是测试它是否是Deletable:来获得同样的结果。

public class ShapeDao { 

    // other dao methods 
    
    public boolean delete(Object object) {
        if (!(object instanceof Shape)) {
            return false;
        }
    
        // delete implementation details
        
        return true;
    }
}

So why create a marker interface when we can achieve the same results using a typical interface?

那么,既然我们可以用一个典型的界面达到同样的效果,为什么还要创建一个标记性的界面呢?

Let’s imagine that, in addition to the Shape type, we want to remove the Person type from the database as well. In this case, there are two options to achieve that.

让我们设想一下,除了Shape类型外,我们还想从数据库中删除Person类型。在这种情况下,有两个选项可以实现这一点。

The first option is to add an additional check to our previous delete() method to verify whether or not the object to delete is an instance of Person:

第一个方案是在我们之前的delete()方法中添加一个额外的检查来验证要删除的对象是否是Person的实例。

public boolean delete(Object object) {
    if (!(object instanceof Shape || object instanceof Person)) {
        return false;
    }
    
    // delete implementation details
        
    return true;
}

But what if we have more types that we want to remove from the database as well? Obviously, this won’t be a good option because we have to change our method for every new type.

但是如果我们有更多的类型也想从数据库中删除呢?显然,这不是一个好的选择,因为我们必须为每个新的类型改变我们的方法

The second option is to make the Person type implement the Shape interface, which acts as a marker interface. But is a Person object really a Shape? The answer is clearly no, and that makes the second option worse than the first one.

第二个选择是使Person类型实现Shapeinterface,它充当了一个标记接口。但是Person对象真的是一个Shape吗?答案显然是否定的,这使得第二个选项比第一个选项更糟糕。

Consequently, although we can achieve the same results by using a typical interface as a marker, we’ll end up with a poor design.

因此,尽管我们可以通过使用一个典型的界面作为标记来达到相同的结果,但我们最终会得到一个糟糕的设计。

7. Conclusion

7.结语

In this article, we learned about marker interfaces and how to use them. Then we explored some built-in Java examples of this type of interface, and how the JDK uses them.

在这篇文章中,我们了解了标记器接口以及如何使用它们。然后我们探讨了这种类型的接口的一些内置的Java例子,以及JDK如何使用它们。

Next, we created our own marker interface and weighed it against using an annotation. Finally, we demonstrated why it’s good practice to use a marker interface in some scenarios instead of a traditional interface.

接下来,我们创建了自己的标记界面,并对其与使用注释进行了权衡。最后,我们展示了为什么在某些情况下使用标记界面而不是传统界面是一个好的做法。

As always, the code can be found on GitHub.

一如既往,代码可以在GitHub上找到。