Sure, that sounds dramatic, but I really think that OOP reached it's peak and is on the decline. In the last time we saw increasing interest in functional programming languages and concepts from functional programming (like closures, continuations etc). Many people want to see those concepts available in well known OOP-languages like Java, C#, C++ etc. And the language designers reacts and are starting to integrate those features.
But will this really ease software development? I doubt it. Feature overkill never was a solution - in nearly every are of life. Only adding feature after feature into something don't makes it better. It much more important that someone creates a homogeneous, useful and practical combination of features. To make programming as easy as possible, the programming language should guide the programmer in how to tackle a certain problem. If you have two different paradigm's available in the language how can you decide which paradigm you should use? And if you have to use 3rd party code, how probable is it that they used the same paradigm you used in your application. Fitting code in different paradigms together creates a so called 'impedance mismatch' which often leads do error prone interface-code (think of OR-mappers to fit a declarative relational language like SQL to a OOP-language like Java).
But if 'multi-paradigm' languages are bad (because they don't guide programmers and make code reuse more difficult) what's the appeal of those features from the world of functional programming? It's simply because OOP has it's limitations and those limitations are starting to become more and more obvious.
I will try to look at some of the reasons why OOP failed to live up to its promises and why I think that OOP will be superseded in the future.
One of this limitations is the 'specialty' of the 'this'/'self' parameter. A method call has a specific parameter which is used for method-dispatch: Determining the method to call at runtime. But what if you need two parameters to determine this method? In OOP you can use the visitor pattern, but it requires lots of boiler-plate code. And if you have 3 or more parameter or even want to decide on more complex conditions (like value ranges, structural constraints etc) it's totally useless.
In functional programming it's different: The central element is the function and all parameters are similar. To dispatch between different implementations at runtime most fp-languages support pattern matching which is much more powerful even then multiple-dispatch because it allows dispatch on the deep structure of the data and not only on the type. But even much more simple problems show the problem with using methods:
Lets look at the Java-runtime-libs. Imagine you want to add a new method to a class from those libs. Maybe you want to have a method 'trimLeft' for Strings which works like the standard 'trim' but only trims the left side of the String. It's not possible. You can't extend String because it's final (for otherwise good reasons), and even if you could, all the String you don't create by yourself are 'standard-strings' without a trimLeft method. So the only way to do it is to invent a new class, maybe 'StringUtils' and put your 'trimLeft' method as static method in it. So every time you have want to trim a string you have to remember that 'String.trimLeft' doesn't exists and that you have to call StringUtils.trimLeft(string) instead.
In a functional (and even procedural) language that's not a problem: Because all operations are implemented via functions, you simply define
function trimLeft(s: String);
and that's it. Totally equal to a function which resides in a lib, and you can import your own extension module wherever you want and use it for every String.
Ok, the Python/Ruby/Smalltalk/etc. user will point out, how easy it is in their favorite language to extend a given class later. But even this has it's disadvantages, because it only works in dynamically typed languages and there are some very valid reasons why dynamical (or latent) typing is a bit problematic. Also it don't solves the problem that 'self' isn't equal to the other parameters.
Look at a simple operation like the addition. In OOP you write something like v1.add(v2). This is totally unsymmetric and don't reflects the structure of the addition. Compare this to the procedural/functional solution:
function add(v1: Int, v2: Int):Int;
function add(v1: Float, v2: Int):Float;
function add(v1: Int, v2: Float):Float;
function add(v1: Float, v2: Float):Float;
As easy as it gets and all adds are defined together at one point in your program. And if you decide to extend 'add' with your own type, for example a new type 'Complex', you simply add some new definitions in your library. In a typical OOP-Language you have to modify Int.add instead, and Float.add, and BigInt.add etc. Even if it's possible, it's still lots of work and it's non-local, the changes are in several classes instead of being bundled in one single module.
Because of this OOP is in fact less extensible as functional and even procedural programming. Sure, there are ways to solve this problem (think of C++'s friend functions for example), but those turn out quite often as borrowed from fp-land and are often conflicting with other parts of the language.
Also have look at Part 2 and Part 3 of this series.