Returning interfaces in Java without significant casting I am writing some code that takes and returns interfaces following the patterns in Effective Java Third Edition. I understand the principle - we want to take in interfaces because in many cases we don't really care about the discrete implementation but rather just the features of this interface. We return interfaces for the same reason. It gives the caller the ability to use the preferred implementation class. Take this extreme contrived example:
import java.util.*;

public class Main
{
    public static List&lt;Integer&gt; op(List&lt;Integer&gt; is) {
        ArrayList&lt;Integer&gt; results = new ArrayList&lt;&gt;();
        
        for (Integer i : is) {
            results.add(i + 1);
        }
        
        return results;
    }
    
    public static void main(String[] args) {
        ArrayList&lt;Integer&gt; nums = new ArrayList(Arrays.asList(1,2,3,4));

        // This line
        ArrayList&lt;Integer&gt; results = (ArrayList&lt;Integer&gt;) op(nums);
        
        System.out.println(results);
    }
}

So if the caller of op wanted to use a LinkedList they could through simple casting. But to me this feels clunky. In my codebase it is littered with various casts that seem to hinder readability.
Is there a smarter way to do this? Or is this explicit casting the only way to do this properly?
Let me clarify something fist

It gives the caller the ability to use the preferred implementation class.

Even if we return interface from method, that doesn't mean that that caller can choose implementation.'

So if the caller of op wanted to use a LinkedList they could through simple casting.

If you mean something like (LinkedList&lt;Integer&gt;) op(nums), then this wont work and will throw java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.util.LinkedList.
When method returns interface, in general, you shouldn't assume specific implementation, and thus should avoid such castings. Having interface here, for example, will give more freedom to the author of this method for future changes: imagine someone will write SuperPerfectList which works better than any other list and author decides to use it. In this case even if method changes, we won't need to refactor every place in our code where we call this method, because signature of this method hasn't changed.
If we talk about the situation where we want another implementation of this interface, then, in case of list, we can do new LinkedList(result) and get what we want.