Интерфейси на маркери в Java

1. Въведение

В този бърз урок ще научим за интерфейсите на маркери в Java.

2. Интерфейси на маркери

Интерфейсът на маркер е интерфейс, който няма методи или константи вътре . Той предоставя информация за типа изпълнение за обекти , така че компилаторът и JVM имат допълнителна информация за обекта .

Интерфейсът на маркера се нарича още интерфейс за маркиране.

Въпреки че интерфейсите с маркери все още се използват, те много вероятно сочат към миризма на код и трябва да се използват внимателно. Основната причина за това е, че те размиват линиите за това какво представлява интерфейсът, тъй като маркерите не дефинират никакво поведение. По-новата разработка благоприятства анотациите за решаване на едни и същи проблеми.

3. Интерфейси JDK Marker

Java има много вградени маркери интерфейси, като Serializable , Cloneable и Remote.

Да вземем примера на интерфейса Cloneable . Ако се опитаме да клонираме обект, който не изпълнява този интерфейс, JVM изхвърля CloneNotSupportedException . Следователно, Cloneable маркерният интерфейс е индикатор за JVM, който можем да наречем метода Object.clone () .

По същия начин, когато извиква метода ObjectOutputStream.writeObject () , JVM проверява дали обектът реализира интерфейса на сериализуем маркер . Когато случаят не е такъв, се изхвърля NotSerializableException . Следователно обектът не е сериализиран към изходния поток.

4. Персонализиран интерфейс на маркера

Нека създадем собствен интерфейс за маркери.

Например, бихме могли да създадем маркер, който показва дали даден обект може да бъде премахнат от базата данни:

public interface Deletable { }

За да изтрие обект от базата данни, обектът, представляващ този обект, трябва да внедри нашия интерфейс за изтриване на маркер:

public class Entity implements Deletable { // implementation details }

Да кажем, че имаме DAO обект с метод за премахване на обекти от базата данни. Можем да напишем нашия метод delete () , така че да могат да бъдат изтрити само обекти, изпълняващи нашия интерфейс на маркер :

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

Както виждаме, ние даваме индикация на JVM за поведението по време на изпълнение на нашите обекти. Ако обектът реализира нашия интерфейс за маркери, той може да бъде изтрит от базата данни.

5. Маркерни интерфейси срещу анотации

С въвеждането на анотации Java ни предостави алтернатива за постигане на същите резултати като интерфейсите на маркерите. Освен това, подобно на интерфейсите на маркери, ние можем да прилагаме пояснения към всеки клас и можем да ги използваме като индикатори за извършване на определени действия.

И така, каква е ключовата разлика?

За разлика от анотациите, интерфейсите ни позволяват да се възползваме от полиморфизма . В резултат на това можем да добавим допълнителни ограничения към интерфейса на маркера.

Например, нека добавим ограничение, че само тип Shape може да бъде премахнат от базата данни:

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

В този случай нашият интерфейс за маркери, нека го наречем DeletableShape, ще изглежда по следния начин:

public interface DeletableShape extends Shape { }

Тогава нашият клас ще приложи маркерния интерфейс:

public class Rectangle implements DeletableShape { // implementation details }

Следователно, всички реализации на DeletableShape са и реализации на Shape . Очевидно не можем да направим това, като използваме анотации .

Всяко дизайнерско решение обаче има компромиси и полиморфизмът може да се използва като контрааргумент срещу интерфейсите на маркери. В нашия пример всеки клас, разширяващ Rectangle, автоматично ще внедри DeletableShape.

6. Маркерни интерфейси срещу типични интерфейси

В предишния пример бихме могли да получим същите резултати, като модифицираме нашия 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; } }

И така, защо да създаваме маркер интерфейс, когато можем да постигнем същите резултати, използвайки типичен интерфейс?

Нека си представим, че освен типа Shape , искаме да премахнем и типа Person от базата данни. В този случай има две възможности за постигане на това:

Първата опция е да добавите допълнителна проверка към нашия предишен метод delete () , за да проверите дали обектът за изтриване е екземпляр на Person или не.

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

Но какво, ако имаме повече типове, които също искаме да премахнем от базата данни? Очевидно това няма да е добър вариант, защото трябва да променим метода си за всеки нов тип .

Вторият вариант е да накарате типа Person да реализира интерфейса Shape , който действа като интерфейс на маркер. Но наистина ли е обект на човек форма ? Отговорът е очевидно не и това прави втория вариант по-лош от първия.

Следователно, въпреки че можем да постигнем същите резултати, като използваме типичен интерфейс като маркер, ще се окажем с лош дизайн.

7. Заключение

В тази статия обсъдихме какво представляват интерфейсите на маркери и как те могат да се използват. След това разгледахме някои вградени Java примери за този тип интерфейси и как те се използват от JDK.

След това създадохме собствен интерфейс за маркери и го претеглихме с помощта на анотация. И накрая, накрая виждаме защо е добра практика да се използва интерфейс на маркер в някои сценарии вместо традиционен интерфейс.

Както винаги, кодът може да бъде намерен в GitHub.