boolean < byte < short < char < int < long < float < double
if A is-a B, then you can use an A anywhere you need a B:
B x = new A();
float y = 7;
However, you cannot use a B wherever you need an A:
A x = new B(); // error! a B is not (necessarily) an A!
int y = 1.0; // error! a float is not (necessarily) an int!
You can (but should almost never) cast a B to an A:
B b = ...;
A a = (A) b; // checks at runtime whether b is actually an A, fails if not
double d = ...;
int y = (int) d; // gives the closest int to d;
If you find yourself casting something, think carefully about redesigning your class hierarchy. We'll introduce some new tools soon that can help.
Note: It is safe and occasionally useful to explicitly cast to a supertype. For example, if you want to use double division instead of integer division, you will need to cast one of the operands to a double first:
int i = 1, j = 2;
double d = i/j; // not what you want: first does integer division (giving 0), _then converts to double_
double iAsDouble = i;
double e = iAsDouble / j; // works but is clunky; first convert to double, then use double division
double f = (double) i / j; // a good use of casting.
When creating a subtype, you must satisfy all requirements of the supertype. If the supertype says the method must do x, the subtype may say the method must do both x and y, but it cannot throw out the requirement that the method do x.
One example of this that is present in other languages (though as it turns out, not Java) is that the arguments for the subtype may have more general types than the arguments of the supertype. For example, if type DogLover has a method called love(Dog), then you can (in principle) implement this interface with a method love(Animal).
In fact, Java rules this out, but it does allow a different example, which we will cover tomorrow.