Yes, generic functions and multimethods are one solution to the expression problem, and they're pretty different from Smalltalk-like OOP. It's a shame they aren't more widely adopted - I think only Julia, Nim, and Clojure have them as a feature.
Actually, if not for some historical accidents, we could have Dylan in place of Java today... I sometimes dream about such a world: functional core, CLOS-like generic functions and multimethods, sane multiple inheritance, syntax-case-like macros, native AOT compiler, sane module system... OpenDylan - the implementation I played with - is slow as molasses, but with even a tiny fraction of the work put into Java it could be made fast enough to rival C++. It's one of the most striking wasted chances in the history of programming.