As others have said, it means that your calling code should only know about an abstract parent, NOT the actual implementing class that will do the work.
What helps to understand this is the WHY you should always program to an interface. There's many reasons, but two of the easiest to explain are
1) Testing.
Let's say I have my entire database code in one class. If my program knows about the concrete class, I can only test my code by really running it against that class. I'm using -> to mean "talks to".
WorkerClass -> DALClassHowever, let's add an interface to the mix.
WorkerClass -> IDAL -> DALClass.
So the DALClass implements the IDAL interface, and worker class ONLY calls through this.
Now if we want to write tests for the code, we could instead make a simple class that just acts like a database.
WorkerClass -> IDAL -> IFakeDAL.
2) Reuse
Following the example above, let's say we want to move from SQL Server (which our concrete DALClass uses) to MonogoDB. This would take major work, but NOT if we've programmed to an interface. In that case we just write the new DB class, and change (via the factory)
WorkerClass -> IDAL -> DALClass
to
WorkerClass -> IDAL -> MongoDBClass