{"id":16284107,"url":"https://github.com/lucko/shadow","last_synced_at":"2025-09-17T09:53:37.774Z","repository":{"id":41440028,"uuid":"153955370","full_name":"lucko/shadow","owner":"lucko","description":" An annotation based API for Java reflection.","archived":false,"fork":false,"pushed_at":"2022-09-28T21:33:47.000Z","size":110,"stargazers_count":28,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-17T13:36:29.991Z","etag":null,"topics":["annotation","java","reflection"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lucko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-20T22:54:44.000Z","updated_at":"2025-02-20T12:49:19.000Z","dependencies_parsed_at":"2022-08-27T06:03:32.744Z","dependency_job_id":null,"html_url":"https://github.com/lucko/shadow","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucko%2Fshadow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucko%2Fshadow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucko%2Fshadow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucko%2Fshadow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lucko","download_url":"https://codeload.github.com/lucko/shadow/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244538560,"owners_count":20468737,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotation","java","reflection"],"created_at":"2024-10-10T19:18:20.984Z","updated_at":"2025-09-17T09:53:32.735Z","avatar_url":"https://github.com/lucko.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shadow\n[![Javadocs](https://javadoc.io/badge/me.lucko/shadow.svg)](https://javadoc.io/doc/me.lucko/shadow) [![Maven Central](https://img.shields.io/maven-metadata/v/https/repo1.maven.org/maven2/me/lucko/shadow/maven-metadata.xml.svg?label=maven%20central)](https://search.maven.org/artifact/me.lucko/shadow)\n\nAn annotation based API for Java reflection.\n\nThe system was inspired by the [`Shadow`](http://jenkins.liteloader.com/job/Mixin/javadoc/org/spongepowered/asm/mixin/Shadow.html) feature in the SpongePowered [Mixin](https://github.com/SpongePowered/Mixin) library. The code in this repository is adapted from the package previously built into [lucko/helper](https://github.com/lucko/helper).\n\n### Example\nGiven the following example base class:\n\n```java\npublic class Person {\n    private final String name;\n    private final int age;\n\n    public Person(String name, int age) {\n        this.name = name;\n        this.age = age;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getAge() {\n        return this.age;\n    }\n}\n```\n\nLet's assume we want to increment the `Person`s 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.\n\n#### Normal Java Reflection\nThis can be done using plain old reflection like this.\n\n```java\npublic static void incrementAge(Person person) {\n    Field ageField;\n    try {\n        ageField = Person.class.getDeclaredField(\"age\");\n    } catch (NoSuchFieldException e) {\n        throw new RuntimeException(e);\n    }\n\n    ageField.setAccessible(true);\n\n    try {\n        ageField.setInt(person, ageField.getInt(person) + 1);\n    } catch (IllegalAccessException e) {\n        throw new RuntimeException(e);\n    }\n}\n```\n\n#### Shadow\nHowever, with shadow, our approach is slightly different.\n\nWe start by defining a \"shadow interface\" for the `Person` class.\n\n```java\n@ClassTarget(Person.class)\npublic interface PersonShadow extends Shadow {\n\n    int getAge();\n\n    @Field\n    void setAge(int age);\n\n    default void incrementAge() {\n        setAge(getAge() + 1);\n    }\n}\n```\n\nThe `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.\n\nOnce the shadow interface has been defined, we can use the `ShadowFactory` to obtain a \"shadow\" instance for our person.\n\nThe `incrementAge` method can then be implemented as follows.\n\n```java\npublic static void incrementAge(Person person) {\n    PersonShadow personShadow = ShadowFactory.global().shadow(PersonShadow.class, person);\n    personShadow.incrementAge();\n}\n```\n\nThe shadow approach has a number of key advantages over the plain reflection method.\n\n* The structure of the `Person` class is outlined in one central location - the shadow interface.\n    * If the layout of `Person` changes - we only have to update one obvious place.\n    * The places in our program using the shadow (in this case the `incrementAge` method) aren't cluttered with the details of the person class.\n* 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.\n* The shadow implementation caches the underlying `Field`, `Method` etc instances behind the scenes, we don't have to worry!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucko%2Fshadow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucko%2Fshadow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucko%2Fshadow/lists"}