There's has been some back and forth in the comments, and my feedback generally boils down to the same argument every time:
Your problem, as you describe it, always starts from the really unusual assumption that you don't know what it is you should be doing and would just interact with random available things just because they're available.
That means your development process is that of an unguided projectile. The solution does not come from a design pattern, it comes from refocusing yourself as a developer.
A developer who cannot be trusted to understand (and stick to) what they need to do, can also not be trusted to appropriately judge and implement a design pattern. Therefore, the source of your question renders the direct answer to your question moot.
Disclaimer: I'm not a kotlin dev, but I infer that the concept of generics is the same.
The generic version is exposing less complexity to the algorithm.
This doesn't quite make sense to me. When comparing generic and non-generic class that are otherwise equivalent, then then manipulated object will be manipulated the same way, regardless of whether its type is concrete or generic. If it's not the same, then the classes are not equivalent.
But to me personally, it seems, the generic version is more readable because I'm not distracted by all the possibilities the special class has, but are not used.
Readability can be lowered by either being too vague (abstract) or too specific (concrete). What is the most readable is incredibly contextual and cannot be answered universally.
But I do want to point out that, contrary to your assertion, additional abstractions can most definitely complicate a codebase.
Should one prefer a generic version of a function, even if it's not re-used (yet)?
While I admit this is an oversimplified response, if one should always prefer a generic version, then you would effectively need to make every property/field in every class (or method) generic and you essentially throw out the concept of having any hardcoded type in any class (or method signature), other than the class type (or method return type) itself.
Clearly, that is overkill, and would be immensely unreadable to boot.
Though it seems tautological, generics should be favored where appropriate to do so.
A simple example is a list, whose operations wholeheartedly do not care about the content of the elements themselves, and therefore it is clear from the start that generics are desirable.
In short, generics make sense when you know for a fact that you do not care (nor will ever care) about the concrete type.
I'm unsure if kotlin has generic type constraint where you can pre-emptively limit your generic type to inherit from a given base class or implement a certain interface, but the reasoning on whether to use generics is the same even when using such a constraint.
because I'm not distracted by all the possibilities the special class has
This justification raises a question mark for me. There's an apparent incongruence between the problem and its solution.
Your question seems to be the digital equivalent of covering up the buttons in your car that you don't intend to use today. It's summer, so you won't use the seat heater today, and then you'd rather just wall off the seat heater buttons so you "don't get distracted by the possibility of using the seat heater".
I know that's a silly analogy, but it's essentially the same argument. It relies on an inherent inability to focus on the things that are relevant without actively removing everything else from view.
- If you struggle handling an object without fully utilizing everything it has to offer, that would be problematic for you in many situations, not just cases where generics could "solve" (or rather hide) the issue for you.
- Would you then also not advocate for favoring the base object type (
object in C#, unsure what it is for kotlin) so you don't get "distracted" by being able to use a concrete class' features when you don't need them? That would lead to significant polymorphism abuse in your codebase, or needing to backtrack and refactor when you do realize that you need access to the more concrete type further down the line.
- This can also lead to being oversensitive to interface segregation if you err on the side of actively removing access to any method/property you don't currently need. Just because an interface has more than one method doesn't mean that you must use all methods provided at all times. A calculator has many different operations but you don't have to use all of them for every calculation.
This isn't mean to critique you, but rather to help you identify the underlying source of your concern, and address it in a way that better fits the problem domain. Using generics to hide information seems to be a patchwork solution to a different problem, and patchwork solutions tend to either not solve a problem completely or have unintended further consequences to them.