Эта статья направлена на то, чтобы дать всестороннее введение в механизм отражения Java. Я надеюсь, что благодаря этой статье у вас будет всеобъемлющее понимание соответствующего содержания Java Reflection.
Прежде чем читать эту статью, вы можете обратиться к « повторному пониманию дженериков Java » .
Предисловие
Механизм отражения Java является очень мощной функцией. Размышления можно увидеть во многих крупномасштабных проектах, таких как Spring и Mybatis. Через механизм отражения мы можем получить информацию типа объекта во время работы. Используя эту функцию, мы можем реализовать шаблоны проектирования, такие как заводский режим и прокси -режим, а также могут решить такие проблемы, как общее стирание Java. В этой статье мы применим механизм отражения Java с точки зрения практических применений.
Основная основа
PS: Эта статья требует, чтобы читатели имели определенную степень понимания API механизма отражения. Если вы не подвергались ему ранее, рекомендуется сначала взглянуть на быстрое начало официального документа.
Прежде чем применять механизм отражения, давайте сначала посмотрим, как получить Class отражения, соответствующий объекту. В Java у нас есть три способа получить класс отражения объекта.
Методом GetClass
В Java каждый Object имеет метод getClass . С помощью метода GetClass мы можем получить соответствующий класс отражения этого объекта:
String s = "ziwenxie"; class <?> C = s.getclass ();
Мы также можем назвать статический метод forName класса Class :
Класс <?> C = class.forname ("java.lang.string"); Или мы можем использовать .class напрямую:
Класс <?> C = string.class;
В начале статьи мы упомянули, что одним из основных преимуществ отражения является то, что она позволяет нам получать информацию о типе объекта во время работы. Давайте подробно рассмотрим это с примером.
Во -первых, мы создаем новый интерфейс A под пакетом typeinfo.interfacea :
пакет typeinfo.interfacea; public interface a {void f (); } Затем мы создаем новый интерфейс C в пакете typeinfo.packageaccess . Интерфейс C наследует от интерфейса A , и мы также создали несколько других методов для тестирования. Обратите внимание, что разрешения следующих методов различны.
package typeinfo.packageaccess; import typeinfo.interfacea.a; класс C реализует {public void f () {System.out.println ("public cf ()"); } public void g () {System.out.println ("public cg ()"); } protected void v () {System.out.println ("Защищенный cv ()"); } void u () {system.out.println ("package cu ()"); } private void w () {System.out.println ("private cw ()"); }} открытый класс hiddenc {public static a makea () {return new c (); }} В методе callHiddenMethod() мы используем несколько новых API, где getDeclaredMethod() используется для получения метода, который класс класса относится к объекту в соответствии с именем метода, и затем мы можем запустить связанные с ними методы объекта, вызывая метод invoke() :
Package typeinfo; импорт typeinfo.interfacea.a; import typeinfo.packageaccess.hiddenc; импорт java.lang.reflect.method; открытый класс hiddenimplementation {public static void main (String [] args) создает исключение {a = hiddenc.makea (); af (); System.out.println (a.getClass (). GetName ()); // упс! Отражение по -прежнему позволяет нам позвонить g (): callhiddenmethod (a, "g"); // и даже методы, которые менее доступны! callhiddenmethod (a, "u"); callhiddenmethod (a, "v"); callhiddenmethod (a, "w"); } static void callhiddenmethod (объект a, string methodname) throws exection {method g = a.getClass (). getDeclaredMethod (methodname); G.SetAccessible (True); g.invoke (a); }} Из результатов вывода мы видим, что будь то public , default , protect или pricate , мы можем свободно вызвать его через класс отражения. Конечно, мы просто чтобы показать мощную силу размышлений, и эта техника не рекомендуется в реальной разработке.
public cf () typeinfo.packageaccess.cpublic cg () пакет cu () защищенный cv () private cw () private cw ()
У нас есть следующий бизнес -сценарий. У нас есть общий List<Class<? extends Pet>> . Нам нужно подсчитать, сколько конкретных Pet в этом классе коллекции. Из -за общего стирания Java определенно невозможно обратить внимание на практику, аналогичную List<? extends Pet> , потому что после того, как компилятор выполняет проверку статического типа, JVM будет рассматривать все объекты в коллекции как Pet во время пробега, но он не будет знать, представляет ли Pet Cat или Dog , поэтому тип информации объекта фактически теряется во время пробега. PS: Об общей стирании: у меня есть подробное объяснение в предыдущей статье. Заинтересованные друзья могут посмотреть.
Чтобы реализовать наш пример выше, мы сначала определяем несколько классов:
открытый класс PET расширяет индивидуальное {public pet (string name) {super (имя); } public pet () {super (); / } public Cat () {super (); }} public Class Dog расширяет PET {public Dog (String name) {super (имя); }} открытый класс Egyptianmau Extens Cat {public egyptianmau (String name) {super (name); } public egyptianmau () {super (); }} открытый класс mutt extends dog {public mutt (string name) {super (name); } public mutt () {super (); }} Класс Pet выше наследует от Individual . Реализация Individual класса немного сложнее. Мы реализовали Comparable интерфейс и переопределили правила сравнения классов. Если мы не очень хорошо это понимаем, это не имеет значения. Мы абстрагировали это, поэтому не имеет значения, если мы не понимаем принцип реализации.
Общедоступные индивидуальные реализации сопоставимы <индивидуальный> {частный статический длинный счетчик = 0; Приватный финальный Long ID = counter ++; Приватное название строки; // имя является необязательным публичным человеком (String name) {this.name = name; } public perency () {} public String toString () {return getClass (). getSiMplename () + (name == null? "": "" + name); } public long id () {return id; } public boolean equals (Object o) {return o ancessionOf индивидуального и id == ((индивидуальное) o) .id; } public int hashcode () {int result = 17; if (name! = null) {result = 37 * result + name.hashcode (); } result = 37 * result + (int) id; результат возврата; } public int compareto (индивидуальная arg) {// сравнить по имени класса сначала: string first = getClass (). getSiMplename (); String argFirst = arg.getClass (). GetSiMplename (); int firstcompare = first.compareto (argfirst); if (firstcompare! = 0) {return firstcompare; } if (name! = null && arg.name! = null) {int secondaryCompare = name.compareto (arg.name); if (secendcompare! = 0) {return econdatorCompare; }} return (arg.id <id? -1: (arg.id == id? 0: 1)); }} Ниже приведен абстрактный класс PetCreator . В будущем мы сможем напрямую получить сбор соответствующих классов Pet , вызывая метод arrayList() . Здесь мы используем метод newInstance() , который мы не упомянули выше. Это вернет экземпляр класса, на который действительно ссылается класс класса. Что это значит? Например, объявление new Dog().getClass().newInstance() и Direct new Dog() эквивалентны.
Public Abstract Class Petcreator {Private Random Rand = новый случайный (47); // Список различных GetTypes PET для создания: Public Abstract List <Class <? Распространяет PET >> getTypes (); public Pet randompet () {// Создать один случайный pet int n = rand.nextint (getTypes (). size ()); try {return getTypes (). get (n) .newinStance (); } catch (instantiationException e) {бросить новое runtimeexception (e); } catch (allogalaccessexception e) {бросить новое runtimeexception (e); }} public pet [] createarray (int size) {pet [] result = new pet [size]; for (int i = 0; i <size; i ++) {result [i] = randompet (); } return Result; } public ArrayList <PET> arrayList (int size) {ArrayList <PET> result = new ArrayList <PET> (); Collections.addall (результат, Createarray (размер)); результат возврата; }} Далее, давайте реализуем приведенный выше абстрактный класс и объясним следующий код. В следующем коде мы объявляем два класса сбора, allTypes и types , среди которых allTypes содержит все классы, объявленные выше, но наши конкретные типы на самом деле являются только двумя типами, а именно Mutt и EgypianMau , поэтому PET, который нам действительно нужен, - new только типы, содержащиеся в types . В будущем мы сможем получить типы, содержащиеся в types , вызывая getTypes() .
Public Class LiteralPetCreator Extens PetCreator {@suppresswarnings («Не контролировал») Общественный статический окончательный список <class <? Extens Pet >> alltypes = collections.unmodifiablelist (arrays.aslist (pet.class, dog.class, cat.class, mutt.class, egyptianmau.class)); Частный статический окончательный список <класс <? Extens Pet >> types = alltypes.sublist (alltypes.indexof (mutt.class), alltypes.size ()); Общественный список <класс <? расширяет PET >> getTypes () {return Types; }} Общая логика была завершена, и, наконец, мы реализуем класс TypeCounter , используемый для подсчета количества соответствующих классов Pet в наборе. Объясните метод isAssignalbeFrom() , который может определить, что класс отражения является подклассом или косвенным подклассом класса отражения. Как следует из названия, getSuperclass() - это получить родительский класс класса отражения.
Public Class TypeCounter Extends HashMap <Class <?>, Integer> {Private Class <?> BASETYPE; public TypeCounter (Class <?> BASETYPE) {this.basetype = basetype; } public void count (Object obj) {class <?> type = obj.getClass (); if (! basetype.isassignablefrom (type)) {бросить новый runtimeexception (obj + "неправильный тип" + type + ", должен быть тип или подтип" + basetype); } countclass (type); } private void countclass (class <?> type) {integer suctity = get (type); положить (тип, количество == null? 1: количество + 1); Класс <?> Superclass = type.getSuperClass (); if (superclass! = null && basetype.isassignablefrom (superclass)) {countclass (superclass); }} @Override public String toString () {stringBuilder result = new StringBuilder ("{"); for (map.Entry <class <?>, Integer> Pair: intrySet ()) {result.append (pare.getKey (). getSiMplename ()); result.append ("="); result.append (pair.getValue ()); result.append (","); } result.delete (result.length () - 2, result.length ()); result.append ("}"); return result.tostring (); }}Суммировать
Выше приведено содержимое этой статьи о примере обмена кодом механизма отражения Java, и я надеюсь, что это будет полезно для всех. Заинтересованные друзья могут продолжать ссылаться на этот сайт:
Java программирование и печать код реализации квитанции о покупках
Подробное объяснение реализации ссылок и динамического прокси на Java
Java программирование для реализации простого обмена кодом лунного затмения
Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это. Спасибо, друзья, за вашу поддержку на этом сайте!