Respecting levels of abstraction means that all the code in a given block (a given function, an interface, an object, or an implementation) is at the same abstraction level. Said differently, abstraction levels are respected if, at a given level, there isn’t any code that is significantly more "low level" then the surrounding code.
It's one of the core insights for writing clean code it underlies a lot of other principles
[[functions should do one thing]]
[[Choose Names at the Appropriate level of abstraction]]
extracting variables works a bit less well then extracting functions - the details are still close by
[[cohesion is having only one abstraction at a given place]]
[[good code/names should be consistent with the level of abstraction they are used in]]
It's a pretty good introduction to the general idea.
Therefore, breaking encapsulation means providing information that goes beyond the abstraction level of the interface.
polymorphism consists of segregating levels of abstraction.
Indeed, for a given interface (or abstract class) and concrete implementation, the base class is abstract and the implementation is less abstract, because the base class describes what it does as an interface, while the implementation code describes how it does it.
Note that the derived class is still somewhat abstract since it is not expressed in terms of 0 and 1, but it is at an inferior level of abstraction compared to the base class. The base class represents what the interface offers, and the derived class represents how it is implemented:
public interface Stack {
Object pop() throws EmptyException;
void push(Object o) throws FullException;
double percentFull();
class EmptyException extends Exception {}
class FullException extends Exception {}
}
stack.percentFull() < 50.0