shadow

An annotation based API for Java reflection

Лицензия

Лицензия

MIT
Группа

Группа

me.lucko
Идентификатор

Идентификатор

shadow
Последняя версия

Последняя версия

1.3
Дата

Дата

Тип

Тип

jar
Описание

Описание

shadow
An annotation based API for Java reflection
Ссылка на сайт

Ссылка на сайт

https://github.com/lucko/shadow
Система контроля версий

Система контроля версий

https://github.com/lucko/shadow

Скачать shadow

Как подключить последнюю версию

<!-- https://jarcasting.com/artifacts/me.lucko/shadow/ -->
<dependency>
    <groupId>me.lucko</groupId>
    <artifactId>shadow</artifactId>
    <version>1.3</version>
</dependency>
// https://jarcasting.com/artifacts/me.lucko/shadow/
implementation 'me.lucko:shadow:1.3'
// https://jarcasting.com/artifacts/me.lucko/shadow/
implementation ("me.lucko:shadow:1.3")
'me.lucko:shadow:jar:1.3'
<dependency org="me.lucko" name="shadow" rev="1.3">
  <artifact name="shadow" type="jar" />
</dependency>
@Grapes(
@Grab(group='me.lucko', module='shadow', version='1.3')
)
libraryDependencies += "me.lucko" % "shadow" % "1.3"
[me.lucko/shadow "1.3"]

Зависимости

test (3)

Идентификатор библиотеки Тип Версия
org.checkerframework : checker-qual jar 2.4.0
org.junit.jupiter : junit-jupiter-api jar 5.1.0
org.junit.jupiter : junit-jupiter-engine jar 5.1.0

Модули Проекта

Данный проект не имеет модулей.

shadow

Javadocs Maven Central

An annotation based API for Java reflection.

The system was inspired by the Shadow feature in the SpongePowered Mixin library. The code in this repository is adapted from the package previously built into lucko/helper.

Example

Given the following example base class:

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

Let's assume we want to increment the Persons age on their birthday. The class is immutable, and doesn't allow us to modify the age once constructed - so, we need to use reflection to change the value of the field.

Normal Java Reflection

This can be done using plain old reflection like this.

public static void incrementAge(Person person) {
    Field ageField;
    try {
        ageField = Person.class.getDeclaredField("age");
    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    }

    ageField.setAccessible(true);

    try {
        ageField.setInt(person, ageField.getInt(person) + 1);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

Shadow

However, with shadow, our approach is slightly different.

We start by defining a "shadow interface" for the Person class.

@ClassTarget(Person.class)
public interface PersonShadow extends Shadow {

    int getAge();

    @Field
    void setAge(int age);

    default void incrementAge() {
        setAge(getAge() + 1);
    }
}

The getAge method simply mirrors the existing method defined on the Person class - nothing special going on there. However, the setAge method is bound to the age field.

Once the shadow interface has been defined, we can use the ShadowFactory to obtain a "shadow" instance for our person.

The incrementAge method can then be implemented as follows.

public static void incrementAge(Person person) {
    PersonShadow personShadow = ShadowFactory.global().shadow(PersonShadow.class, person);
    personShadow.incrementAge();
}

The shadow approach has a number of key advantages over the plain reflection method.

  • The structure of the Person class is outlined in one central location - the shadow interface.
    • If the layout of Person changes - we only have to update one obvious place.
    • The places in our program using the shadow (in this case the incrementAge method) aren't cluttered with the details of the person class.
  • We don't have to deal with the checked exceptions associated with obtaining the field or modifying the value. These are simply wrapped up into a RuntimeException thrown when the shadow is obtained.
  • The shadow implementation caches the underlying Field, Method etc instances behind the scenes, we don't have to worry!

Версии библиотеки

Версия
1.3
1.2
1.1
1.0