package powerups;

import models.Activation;
import models.Club;
import models.PowerupModel;
import play.twirl.api.Html;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public abstract class Powerup implements Serializable {

    /**
     * Private access as no Powerup should ever be able to change the name of a Club.
     */
    private final Club club;

    /**
     * Final, as no view should ever have access to modify its own model runtime.
     */
    public final PowerupModel model;

    /**
     * Boilerplate constructor used to set up a Powerup. This exposes some vital information for all Powerups.
     * @param club The club referenced by the {@link models.Activation} used to instantiate the Powerup. It is saved
     *             for reference.
     * @param model The model referenced by the {@link models.Activation} used to instantiate the Powerup. It is saved
     *              so that views may access the data held by the model.
     * @see play.db.ebean.Model
     */
    public Powerup(Club club, PowerupModel model) {
        this.club = club;
        this.model = model;
    }

    /**
     * Every powerup must implement a method to render its associated view. For now, only one view may be referenced,
     * though this is likely to change as the API matures.
     * @return The HTML to insert into the club's view.
     * @see views.html.club.show
     * @see play.twirl.api.Html
     */
    public abstract Html render();

    /**
     * Lazily loads the {@link powerups.Powerup} associated with the {@link models.Activation}
     * @param activation The activation used to instantiate the Powerup.
     * @return The instantiated Powerup.
     */
    public static Powerup getPowerup(Activation activation) {
        PowerupModel powerupModel = PowerupModel.find.byId(activation.key.powerupId);
        Club club = Club.find.byId(activation.key.clubId);

        try {
            @SuppressWarnings("unchecked")
            Class<? extends Powerup> c = (Class<? extends Powerup>) Class.forName("powerups." + powerupModel.className);
            Constructor<? extends Powerup> constructor = c.getDeclaredConstructor(Club.class, PowerupModel.class);
            return constructor.newInstance(club, powerupModel);
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected Club getClub() {
        return club;
    }
}

package powerups;

import models.Activation;
import models.Club;
import models.PowerupModel;
import play.twirl.api.Html;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * The class that every Powerup must extend. The nature of a powerup is fickle. Once its class name has been set in its
 * {@link models.PowerupModel}, this should never change post production. The class uses lazy loading to read its
 * model's class name, and thus must be properly set and qualified as documented in the PowerupModel class. Make sure
 * to properly read example powerups' documentation, as well as this one before attempting to use write a powerup.
 * As of 1.0-SNAPSHOT, there is no error handling for the powerup activation, so a user will not get any indication
 * whether this powerup exists if it is not loaded properly.
 * @since 1.0-SNAPSHOT
 * @see models.PowerupModel
 */
public abstract class Powerup implements Serializable {

    /**
     * Private access as no Powerup should ever be able to change the name of a Club.
     */
    private final Club club;

    /**
     * Final, as no view should ever have access to modify its own model runtime.
     */
    public final PowerupModel model;

    /**
     * Boilerplate constructor used to set up a Powerup. This exposes some vital information for all Powerups.
     * @param club The club referenced by the {@link models.Activation} used to instantiate the Powerup. It is saved
     *             for reference.
     * @param model The model referenced by the {@link models.Activation} used to instantiate the Powerup. It is saved
     *              so that views may access the data held by the model.
     * @see play.db.ebean.Model
     */
    public Powerup(Club club, PowerupModel model) {
        this.club = club;
        this.model = model;
    }

    /**
     * Every powerup must implement a method to render its associated view. For now, only one view may be referenced,
     * though this is likely to change as the API matures.
     * @return The HTML to insert into the club's view.
     * @see views.html.club.show
     * @see play.twirl.api.Html
     */
    public abstract Html render();

    /**
     * Lazily loads the {@link powerups.Powerup} associated with the {@link models.Activation}
     * @param activation The activation used to instantiate the Powerup.
     * @return The instantiated Powerup.
     */
    public static Powerup getPowerup(Activation activation) {
        PowerupModel powerupModel = activation.powerup;
        Club club = activation.club;

        try {
            @SuppressWarnings("unchecked")
            Class<? extends Powerup> c = (Class<? extends Powerup>) Class.forName("powerups." + powerupModel.className);
            Constructor<? extends Powerup> constructor = c.getDeclaredConstructor(Club.class, PowerupModel.class);
            return constructor.newInstance(club, powerupModel);
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected Club getClub() {
        return club;
    }
}

Improve speed of lazy loader, and further document.