简体   繁体   中英

Generic primitive type with casting

I've got a problem with my Java code. I want to make my method universal with using of generics types. It's easier to explain it with code:

public interface Destroyable {

    void destroy();

    default void destroyArray(byte[] array) {
        Arrays.fill(array, (byte)0);
    }

    default void destroyArray(char[] array) {
        Arrays.fill(array, (char)0);
    }

}

The point is, that I want my default method destroyArray to work with any type, like this:

public interface Destroyable {

    void destroy();

    default <E> void destroyArray(Class<E>[] array) {
        Arrays.fill(array, (E)0);
    }

}

It gives me error:

Inconvertible types; cannot cast 'int' to 'E'

Is there any simple solution to achieve this?

Default value of objects

The problem with your code:

default <E> void destroyArray(E[] array) {
    Arrays.fill(array, (E) 0);
}

is that a general E type is of course not necessarily an int (or something that can be autoboxed like Integer ). But you want to actually write some default value. Therefore you would need to create valid instances of E . Which, in general, is hard because you don't know anything about the type, including its constructors.

However, there is one valid value for all Object s, namely null . It indicates that there simply is no instance at the moment.

So the following would work for arbitrary types E :

default <E> void destroyArray(E[] array) {
    Arrays.fill(array, null);
}

Primitives

However, with that you are still not able to fill primitive-type arrays like int[] since E can only be used for Object s, not primitives. You would need to hard-code additional methods for each primitive:

default void destroyArray(int[] array) {
    Arrays.fill(array, 0);
}

default void destroyArray(double[] array) {
    Arrays.fill(array, 0.0);
}

// ...

Note on Class<E>

Your original code had Class<E>[] instead of E[] . Note that Class<E> means something completely different than E[] .

Class<E> is a Class object, a wrapper that provides Reflection-API access to analyze contents of the class . Like getting names of methods and stuff like that. Whereas E is the class itself.

So Person would be a Person class and Class<Person> is like a class which knows stuff about the Person class, like its method names.

See the documentation of Class for more details.

default <E> void destroyArray(E[] array) {
    Arrays.fill(array, null);
}

The above might be what you intended. However , this wouldn't work with primitive types. Because primitive types are not allowed to be passed as generic type parameters, you will still need a destroyByteArray , destroyIntArray etc.

Also, destroyArray doesn't seem to belong in Destroyable . Shouldn't it just be in a helper class full of static methods? If I were you I would move it to there. It does not even call the destroy method, so it has no reason to be in Destroyable .

Your original approach with many overloaded methods is fine.

For primitive types, the generics would work for their arrays as:

default <A> void destroyArray(A array) {
    Class<?> arrayType = array.getClass();
    if (!arrayType.isArray()) {
        return;
    }
    Class<?> type = arrayType.getComponentType();
    if (type == int.class) {
        Arrays.fill((int[])array, 0);
    } else if (type == char.class) {
        Arrays.fill((char[])array, '\0');
    }
    //...
}

As you can see: this is hardly more compact, slower and less type safe, and secure forgotten primitive types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM