To learn, you need to experience learning within the same context as usage.The thing is, the mind will learn with everything you give it. If you want to learn efficiently, give the mind only what it will normally get. You need to give yourself an environment that is not "too" different from the environment that you will experience when you actually need to use the learning.
Now all of us may sometime "learn wrong". That is learn within the wrong context, and then be obliged to bring this context in when we need our learning. For example, the top two lines of your keyboard are typically "badly learned. We make the effort to learn to touch type the alphabet, possibly the numbers, but not all the symbols, and definitely not the function keys (f1...f12). When you have learned "wrong", you must relearn. And to do that you must force yourself to stop using the "bad environment" (using your eyes) until you have learned enough to use the "good environment" (only use fingers), which is the one that matches you productive environment. I had such a hard time "unlearning" looking at my top keys, that I purchased a blank keyboard many years ago. Having used this blank keyboard at work for a few years, I learned that "looking down" was of no use. Now I use a normal keyboard, and yet I still do not look down. First because my first reaction is not too, but also because the feeling of touch typing without help is a great feeling, it is like running or riding a bicycle, and as your fingers move freely you can concentrate on more important things. Also, looking down is breaking that great feeling.
This brings me to programming. For example, you might ask, how do I teach myself to use a new library? My first answer is:
Never use code completion!If I want to learn a new library, first I read the documentation. Or read code that uses the library. Then I usually print out the header files and "study them" a bit. I keep these printouts nearby (yes, sorry for the trees). Then when I start programming. Usually, I will type in example code, which I will change to try out different features of the library. I will always try to guess the right name and usage patterns to use the library. If I feel that I am really not sure enough, I will "physically" make the effort to look at my header printout, or example code. What is important is that this use of external help be somewhat inefficient. Also important, is that you make the effort to remember the names and signature of things (I'll come back to that below).
Now I have noticed a few things. By printing the header files, or main code usages, I am giving my mind a physical reference of what needs to be remembered. And even though I may not use this reference, I will learn better because I know that this reference is there and that its physicality gives it an immutable nature. Then the fact that the printout has a structure, that the definitions follow a certain order, or are on one page or the other is important for the memory process. This brings me back to code completion. Code completion provides little reference for you memory to work with. Yes, you will remember things on the long run, but not as quickly and not as well, and that will make you a less good developer.
Now we can get to the core of the learning process:
Make it personal!If you want to be a good developer, you need to accept that you approach your craft like a chess grandmaster. It is a life long learning process, where you are building layers and layers of learning. And each of these layers of learning are working off each other, in a manner that generates feelings. It is these feelings that cruise you along. Your thinking process works one level above making big decisions on where you are going. Therefore, when you learn a library, you really want to invest the time to learn its signatures, not only the names that provide the different features. This is because these signature will support the feeling that you have when you use the libraries. Without those feelings you will be wasting your time thinking about little details, which is really not what you want to do.
In reality, you do not learn each signature of each type, function, object, module, etc. as a separate learning. Each piece of code is written with a certain style, following styles of others, and following patterns of design and coding. It would be way too hard to learn everything "in separation", so the mind will try to learn things "as a grand scheme". BUT... this only works if you have started along this path in the first place. You need to make the effort from the start to remember the little patterns and names that you meet along your programming lifetime. Of course it is never too late to start later along this way of learning. But if you never approach it this way, then you will never become a master developer.
I am not sure it is much more complicated. The only thing I would add, is to take your time. The thing is, if a piece of code/library is written in a manner that is too far away from your current learning. You will not be able to remember it. Possibly even not be able to understand it. That is when you need to be strategic. You need to have a few long term goals of "areas" to develop. Then spend a lot of your personal time programming "towards" those areas. Finally, when you are close enough, you will find that you can read code, and remember code, that you previously could not.