Java redundant casts required in generic method

First off, this statement is logically wrong

if(type.isInstance(String.class))

If type is Class<String> then isInstance is checking to see if the argument is a string instance. The argument you are passing is a class instance (specifically, a Class<String>).

If you prefer,

String.class.isInstance(String.class) == false

What you meant was

if(type == String.class)

However, even with this logical error resolved, your code will still have an unchecked cast warning.

The part you are missing is right here

Cast to ArrayList<T> is required even though we know that if type String is passed as a parameter it will return ArrayList<String> just like forString() method

Exactly. We know it. But what we know and what the compiler knows are two different things. The compiler is not clever enough to check the conditional and realise that the type is okay. It conceivably could be smart enough, but it is not.

This is precisely why this manifests as a warning and not as an error. It is a warning because what you are doing is potentially wrong; it is not definitely wrong, else it would not compile at all. In this case, the warning should act as a prompt for you to double-check that what you're doing is correct and then you can happily suppress it.

@SuppressWarnings("unchecked")
public static <T> ArrayList<T> function(Class<T> type){
    if(type == String.class)
        return (ArrayList<T>) forString();
    return forGeneric(type);
}

Finally -- and it may be an artifact of your contrived example -- but all of these methods are useless. There does not seem to be any advantage over calling new ArrayList<>() directly. At runtime, the actual instances are identical regardless of which of the 3 methods it came from.