- Learn Java for Android Development: Reflection Basics
- What You’ll Need
- Why Use Reflection?
- Inspecting Classes
- Inspecting the Constructors Available Within a Class
- Inspecting the Fields Available Within a Class
- Inspecting the Methods Available Within a Class
- Inspecting Inner Classes
- Inspecting Member Modifiers
- Inspecting Class Metadata
- Reflection: Handy for Debugging
- Why Not To Use Reflection
- Wrapping Up
- Retrieving Class Objects
- Object.getClass()
- The .class Syntax
- Class.forName()
- TYPE Field for Primitive Type Wrappers
- Methods that Return Classes
- Полное руководство по Java Reflection API. Рефлексия на примерах
- Java Reflection API
- Ограничения при работе с рефлексией в Java
Learn Java for Android Development: Reflection Basics
In this tutorial, you’ll become familiar with the concept of Java reflection: the ability of a class or object to examine details about its own implementation programmatically.
Android applications are written in the Java, a programming language that supports reflection—the ability of an object to examine itself. In this tutorial, you’ll learn the basics of Java reflection, including how to inspect the methods and fields of a given class, check for the availability of specific methods, and other practical tasks you may need to use when developing for different versions of the Android SDK.
What You’ll Need
Technically, you don’t need any tools to complete this tutorial but you will certainly need them to develop Android applications.
To develop Android applications (or any Java applications, for that matter), you need a development environment to write and build applications. Eclipse is a very popular development environment (IDE) for Java and the preferred IDE for Android development. It’s freely available for Windows, Mac, and Linux operating systems.
For complete instructions on how to install Eclipse (including which versions are supported) and the Android SDK, see the Android developer website.
Why Use Reflection?
Reflection gives developers the flexibility to inspect and determine API characteristics at runtime, instead of compile time. Within the security constraints imposed by Java (e.g. use of public, protected, private), you can then construct objects, access fields, and invoke methods dynamically. The Java Reflection APIs are available as part of the java.lang.reflect package, which is included within the Android SDK for developers to use.
So what does this have to do with Android development? Well, with each new version of the Android SDK, classes, interfaces, methods, etc. are added, updated, and (less frequently) removed. However, Android developers often want to target devices running different versions of Android with a simple application package. To do this, Android developers may use reflection techniques to determine, at runtime, if a specific class or method is available before trying to use it. This allows the developer to leverage new APIs where available while still supporting the older devices—all in the same application.
Inspecting Classes
Java classes are represented at runtime using the Class (java.lang.Class) class. This class provides the starting point for all reflection APIs. Within this class, you’ll find many methods for inspecting different aspects of a class, such as its fields, constructors, methods, permissions, and more. You can also use the Class method called forName() to load a non-primitive class (e.g. not int, but Integer) by name dynamically at runtime, instead of at compile time:
The class (in this case, NotificationManager) need not have the corresponding import statement in your code; you are not compiling in this class into your application. Instead, the class loader will load the class dynamically at runtime, if possible. You can then inspect this Class object and use the reflection techniques described in the rest of this tutorial.
Inspecting the Constructors Available Within a Class
You can inspect the constructors available within a given Class. To get just the constructors that are publicly available, use getConstructors(). However, if you want to inspect those methods specifically declared within the class, whether they are public or not, use getDeclaredConstructors() instead. Both methods return an array of Constructor (java.lang.reflect.Constructor) objects.
For example, the following code iterates through the declared constructors of a class:
Once you have a valid Constructor object, you can inspect its parameters and even declare a new instance of the class using that constructor with the newInstance() method.
Inspecting the Fields Available Within a Class
You can inspect the fields (or attributes) available within a given Class. To get just the methods that are publicly available, including inherited fields, use getFields(). However, if you want to inspect those fields specifically declared within the class (and not inherited ones), whether they are public or not, use getDeclaredFields() instead. Both methods return an array of Field (java.lang.reflect.Field) objects.
For example, the following code iterates through the declared fields of a class:
You can also check for a specific public field by name using the getField() method. For example, to check for the EXTRA_CHANGED_PACKAGE_LIST field of the Intent class (which was added in API Level 8, or Android 2.2), you could use:
Once you have a valid Field object, you can get its name using the toGenericString() method. If you have the appropriate permissions, you can also access the value of that class field using the appropriate get() and set() methods.
Inspecting the Methods Available Within a Class
You can inspect the methods available within a given Class. To get just the methods that are publicly available, including inherited methods, use getMethods(). However, if you want to inspect those methods specifically declared within the class (without inherited ones), whether they are public or not, use getDeclaredMethods() instead. Both methods return an array of Method (java.lang.reflect.Method) objects.
For example, the following code iterates through the declared methods of a class:
Once you have a valid Method object, you can get its name using the toGenericString() method. You can also examine the parameters used by the method and the exceptions it can throw. Finally, if you have the appropriate permissions, you can also call the method using the invoke() method.
Inspecting Inner Classes
You can inspect the inner classes defined within a Class using getDeclaredClasses() method. This method will return an array of Class (java.lang.class) objects declared within the parent class. These classes can then be inspected like any other.
Inspecting Member Modifiers
You can also inspect the flags and security settings—called modifiers—associated with a given Class, Field, or Method using the getModifiers() method. Interesting modifiers include whether the component is public, private, protected, abstract, final, or static (amongst others).
For example, the following code checks the security modifiers of a class:
Keep in mind that you cannot dynamically access or invoke any class, method, or field using reflection that you would not normally be able to access at compile-time. In other words, regular class security is still applied at runtime.
Inspecting Class Metadata
You can also inspect the metadata—called annotations—associated with a given class, field or method using the getAnnotations() method. Interesting metadata associated with a class might include information about deprecation, warnings, and overrides, among other things.
For example, the following code checks the metadata available for the AbsoluteLayout class. Since this class was deprecated in Android 1.5, one of the annotations returned is @java.lang.Deprecated() when this code is run on Android 2.2:
Similarly, you could simply check for the existence of a specific annotation, such as deprecation, by its type:
Reflection: Handy for Debugging
You can also use reflection to assist with debugging. For example, you might want to use the class keyword to access the underlying class data for a given type:
You can also get class information from a variable instance using the getClass() method of the Object class (which is therefore inherited by all classes in Java):
If you want to check the class of a variable, using instanceof is more appropriate. See the previous tutorial on instanceof for more details.
Similarly, you might want to use the getClass() method with the this keyword to check the name of the class you’re currently in and include this information as part of your debug logging to LogCat:
Why Not To Use Reflection
As you’ve seen, reflection can be used to great effect, especially when you are unsure if a specific class or method is available at compile time. Reflection does, however, have some drawbacks, including reduced performance and the loss of the strong typing and safe coding practices enforced at compile time. It’s best to use reflection sparingly, but do use it when needed.
Wrapping Up
Reflection is a powerful tool that Java developers can use to explore packages and APIs programmatically at runtime. While reflection operations come at a cost, they give the developer the flexibility that is sometimes essential for getting the job done. Android developers frequently use these simple reflection techniques to test for the availability of specific classes, interfaces, methods, and fields at runtime, enabling them to support different versions.
Источник
Retrieving Class Objects
The entry point for all reflection operations is java.lang.Class . With the exception of java.lang.reflect.ReflectPermission , none of the classes in java.lang.reflect have public constructors. To get to these classes, it is necessary to invoke appropriate methods on Class . There are several ways to get a Class depending on whether the code has access to an object, the name of class, a type, or an existing Class .
Object.getClass()
If an instance of an object is available, then the simplest way to get its Class is to invoke Object.getClass() . Of course, this only works for reference types which all inherit from Object . Some examples follow.
Returns the Class for String
There is a unique console associated with the virtual machine which is returned by the static method System.console() . The value returned by getClass() is the Class corresponding to java.io.Console .
A is an instance of the enum E ; thus getClass() returns the Class corresponding to the enumeration type E .
Since arrays are Objects , it is also possible to invoke getClass() on an instance of an array. The returned Class corresponds to an array with component type byte .
In this case, java.util.Set is an interface to an object of type java.util.HashSet . The value returned by getClass() is the class corresponding to java.util.HashSet .
The .class Syntax
If the type is available but there is no instance then it is possible to obtain a Class by appending «.class» to the name of the type. This is also the easiest way to obtain the Class for a primitive type.
Note that the statement boolean.getClass() would produce a compile-time error because a boolean is a primitive type and cannot be dereferenced. The .class syntax returns the Class corresponding to the type boolean .
The variable c will be the Class corresponding to the type java.io.PrintStream .
The .class syntax may be used to retrieve a Class corresponding to a multi-dimensional array of a given type.
Class.forName()
If the fully-qualified name of a class is available, it is possible to get the corresponding Class using the static method Class.forName() . This cannot be used for primitive types. The syntax for names of array classes is described by Class.getName() . This syntax is applicable to references and primitive types.
This statement will create a class from the given fully-qualified name.
The variable cDoubleArray will contain the Class corresponding to an array of primitive type double (that is, the same as double[].class ). The cStringArray variable will contain the Class corresponding to a two-dimensional array of String (that is, identical to String[][].class ).
TYPE Field for Primitive Type Wrappers
The .class syntax is a more convenient and the preferred way to obtain the Class for a primitive type; however there is another way to acquire the Class . Each of the primitive types and void has a wrapper class in java.lang that is used for boxing of primitive types to reference types. Each wrapper class contains a field named TYPE which is equal to the Class for the primitive type being wrapped.
There is a class java.lang.Double which is used to wrap the primitive type double whenever an Object is required. The value of Double.TYPE is identical to that of double.class .
Void.TYPE is identical to void.class .
Methods that Return Classes
There are several Reflection APIs which return classes but these may only be accessed if a Class has already been obtained either directly or indirectly.
Источник
Полное руководство по Java Reflection API. Рефлексия на примерах
В этой статье мы познакомимся со всеми элементами и функциональными возможностями Java Reflection API.
Рефлексия в Java — это механизм, с помощью которого можно вносить изменения и получать информацию о классах, интерфейсах, полях и методах во время выполнения, при этом не зная имен этих классов, методов и полей. Кроме того, Reflection API дает возможность создавать новые экземпляры классов, вызывать методы, а также получать или устанавливать значения полей.
Начинающие Java-программисты часто путают рефлексию с интроспекцией. Интроспекция — проверка кода и возможность видеть типы объектов во время выполнения. Рефлексия дает возможность вносить изменения во время выполнения программы путем использования интроспекции. Здесь важно понимать различие, поскольку некоторые языки поддерживают интроспекцию, но не поддерживают рефлексию, например, C++.
В этом руководстве мы рассмотрим не только базовые, но и более продвинутые возможности Reflection API.
Java Reflection API
Рефлексия — мощная концепция, которая лежит в основе большинства современных Java/Java EE фреймворков и библиотек. Например, для Java классическими примерами являются:
- JUnit – фреймворк для модульного тестирования. Он использует рефлексию для парсинга аннотаций (например, @Test ) для получения описанных программистом тестовых методов и дальнейшего их выполнения.
- Spring – фреймворк для разработки приложений на Java платформе, в основе которого лежит внедрение зависимостей (инверсия управления).
Список можно продолжать бесконечно: от веб-контейнеров до решения задач объектно-реляционного отображения (ORM). Их всех объединяет одно: они используют Java рефлексию, потому что не имеют доступа и представления к определенных пользователем классах, методах, интерфейсах и т.д.
Ограничения при работе с рефлексией в Java
Почему мы не должны использовать рефлексию в обычном программировании, когда уже есть доступ к интерфейсам и классам. Причин несколько:
- Низкая производительность — поскольку рефлексия в Java определяет типы динамически, то она сканирует classpath, чтобы найти класс для загрузки, в результате чего снижается производительность программы.
- Ограничения системы безопасности — рефлексия требует разрешения времени выполнения, которые не могут быть доступны для систем, работающих под управлением менеджера безопасности (Java Security Manager).
- Нарушения безопасностиприложения — с помощью рефлексии мы можем получить доступ к части кода, к которой мы не должны получать доступ. Например, мы можем получить доступ к закрытым полям класса и менять их значения. Это может быть серьезной угрозой безопасности.
- Сложность в поддержке — код, написанный с помощью рефлексии трудно читать и отлаживать, что делает его менее гибким и трудно поддерживаемым.
Источник