class KeyImpl<T> extends Key<T> {
        KeyImpl(Type type, Object qualifier) {
            super(type, qualifier);
        }
    }

    public static <T> Key<T> of(Class<T> type) {
        return new KeyImpl<>(type, null);
    }

    public static <T> Key<T> of(Class<T> type, @Nullable Object qualifier) {
        return new KeyImpl<>(type, qualifier);
    }

    public static <T> Key<T> ofType(Type type) {
        return new KeyImpl<>(type, null);
    }

    public static <T> Key<T> ofType(Type type, @Nullable Object qualifier) {
        return new KeyImpl<>(type, qualifier);
    }

    private Type getTypeParameter() {
        // this cannot possibly fail so not even a check here
        Type typeArgument = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        Object outerInstance = ReflectionUtils.getOuterClassInstance(this);
        //		// the outer instance is null in static context
        return outerInstance != null
                ? Types.bind(typeArgument, Types.getAllTypeBindings(outerInstance.getClass()))
                : typeArgument;
    }

    public Type getType() {
        return type;
    }

    /**
     * A shortcut for <code>{@link Types#getRawType(Type)}(key.getType())</code>.
     * Also casts the result to a properly parameterized class.
     */
    @SuppressWarnings("unchecked")
    public Class<T> getRawType() {
        return (Class<T>) Types.getRawType(type);
    }

    /**
     * Returns a type parameter of the underlying type wrapped as a key with no qualifier.
     *
     * @throws IllegalStateException when underlying type is not a parameterized one.
     */
    public <U> Key<U> getTypeParameter(int index) {
        if (type instanceof ParameterizedType) {
            return new KeyImpl<>(((ParameterizedType) type).getActualTypeArguments()[index], null);
        }
        throw new IllegalStateException("Expected type from key " + getDisplayString() + " to be parameterized");
    }

    public @Nullable Object getQualifier() {
        return qualifier;
    }

    /**
     * Returns an underlying type with display string formatting (package names stripped)
     * and prepended qualifier display string if this key has a qualifier.
     */
    public String getDisplayString() {
        return (qualifier != null ? Utils.getDisplayString(qualifier) + " " : "")
                + ReflectionUtils.getDisplayName(type);
    }

    @Override
    public boolean equals(Object o) {<FILL_FUNCTION_BODY>

        if (this == o) {
            return true;
        }
        if (!(o instanceof Key<?>)) {
            return false;
        }
        Key<?> that = (Key<?>) o;
        return type.equals(that.type) && Objects.equals(qualifier, that.qualifier);
    
<no_super_class>