I want to replace the if-else function with a one-liner return statement, and I was wondering why I can't create an object via contructor reference within a ternary operator?
What I would like to replace something like this:
public Animal createAnimal(boolean isDangerous) {
if(isDangerous) {
Shark shark = new Shark(); // Shark extends Animal
return shark ;
}
Cat cat = new Cat(); //Cat extends Animal
return cat;
}
This runs fine:
public Animal createAnimal(boolean isDangerous) {
Shark shark = new Shark(); // Shark extends Animal
Cat cat = new Cat(); //Cat extends Animal
return isDangerous ? shark : cat;
}
This is also working from an another part of the code:
Optional<Animal> animalOptional = zoo.getAnimals().stream().findAny(); //getAnimals() is returning a Set<Animal>
animalOptional.orElseGet(isDangerous ? Shark::new : Cat::new); // -> it creates me an Object depending on the boolean
But if I want to return an object there, I got this error message: The target type of this expression must be a functional interface
public Animal createAnimal(boolean isDangerous) {
return isDangerous ? Shark::new : Cat::new;
}
Is there a one-liner solution for the first block?
Is there a one-liner solution for the first block?
Yes, you're overthinking it:
return isDangerous ? new Shark() : new Cat();
The operands of the conditional operator are only evaluated when required, so this will only create one instance.
I got this error message: The target type of this expression must be a functional interface
That's because you're not actually returning an Animal, but rather a method reference to a thing which takes no arguments and returns an Animal. So, you could have written (amongst other things):
public Supplier<Animal> createAnimal(boolean isDangerous) {
return isDangerous ? Shark::new : Cat::new;
}
Simplest one-liner I could think of
public Animal createAnimal(boolean isDangerous) {
return isDangerous ? new Shark() : new Cat();
}
Related
what I have is a list of Dog objects, in the objects there contains a value that is a Boolean to show whether the dog as completed a training or not. What im trying to do is iterate over the list and only return Dog() objects that have completed training, for instance if their are 12 dogs, and only 3 have completed training, the loop should only print those objects.
else if (input == 1) {
for (int i = 0; i < 12; ++i) {
//Create a temporary value to hold the object.
Object tempHold = dogKennel.getAnimal(i);
//If animal has not graduated, skip, else print.
if (!(tempHold.getGraduation())) {
continue;
}
else {
System.out.println(dogKennel);
}
}
getAnimal(i) returns the object at int i
the method .getGraduation is defined and does return a Boolean however the compiler doesnt want to recognize temp value as is, and doesnt go beyond that value. the compiler keeps suggesting to cast tempHold, but even if I do, it doesnt work.
i feel like it would work if i could get it to compile, as the object that is returned would have a getGraduation() method (it is defined for the super class of the animal.)
however the compiler doesn't want to recognize temp value as is, ...the compiler keeps suggesting to cast tempHold, but even if I do, it doesn't work.
The compiler is telling you two things:
An Object is NOT a Dog (the opposite is true. A Dog is an Object).
class Object does not have method isGraduated() defined for them.
To fix this, you can cast Object to Dog:
Dog tempHold = (Dog)dogKennel.getAnimal(i);
Now that we have a Dog, we can safely invoke isGraduated() on it. But the problem is we canNOT be sure that we have a Dog. We may as well have a Cat if we get the Animal from another kennel. In that case, you will get a ClassCastException which tells you that Cats cannot be cast as Dogs.
To avoid getting run time exceptions, you can add a check:
Object tempHold = dogKennel.getAnimal(i);
if(tempHold instanceof Dog) {
Dog dog = (Dog)tempHold;
System.out.println(dog.isGraduated());
}
The instanceof check fixes the problem.
There are ways to avoid this run time check altogether. One would be to create an interface:
public interface CanGraduate {
default boolean isGraduated() {
return false;
};
}
Then make all Animals implement this interface:
public abstract class Animal implements CanGraduate {
//Behavior common among all animals
}
You can now freely add new animal types and be assured that you can safely invoke isGraduated() on them and get a false value as long as they inherit from the above Animal class.
For dogs, isGraduated() is supposed to be more meaningful. So you can override it in their case:
public class Dog extends Animal {
private boolean _graduated = true;
#Override
public boolean isGraduated() {
return _graduated; //or some complex logic that determines graduation
}
}
With this structure, you no longer need to worry about invoking the method on any kind of Animal.
As an example, let us see some driver code:
public class Main {
public static void main(String[] args) {
Dog dog1 = new Dog();
Dog dog2 = new Dog();
Cat cat1 = new Cat();
Cat cat2 = new Cat();
List<Animal> dogKennel = List.of(dog1, dog2);
List<Animal> catKennel = List.of(cat1, cat2);
for(Animal x : catKennel) {
System.out.println(x.isGraduated());
}
}
}
The program will simply output false since Cats can never graduate. If the kennel contained Dogs, it would output the actual graduation status of the dog.
Like the others have said, the getGraduation() method is only defined presumably in the Dog class. This means that the method can only be called on Objects with the type Dog. To define a variable with type Dog you can do Dog temphold = *whatever*. The reason it wants you to cast is because Object is a supertype of Dog. If you'd like to read more about casting you can here: https://javarevisited.blogspot.com/2012/12/what-is-type-casting-in-java-class-interface-example.html
Basically, all you have to do is a cast to convert the object
if (!(((Dog)tempHold).getGraduation()))
{
continue;
}
this casting tells the compiler that even though tempHold is an object of the Object class it also is an object of the Dog class and should have all of its properties
If you want to filter the list for just ones with a certain condition, the most common way to do that these days is with the Stream::filter method.
It would look like this:
List<Dog> completedTraining =
dogKennel
.stream()
.filter(
dog -> !dog.getGraduation()
)
.collect(
Collectors.toList()
)
;
Suppose I would like to write a method that takes a list and a Function returning another generic type, say Optional. A concrete example for such a method would be one that applies the function to all elements in the list, and returns a list of all elements that didn't result in an empty Optional.
To the best of my understanding, here is how I would write out this method:
public <I, O> List<O> transform(List<I> list, Function<? super I, Optional<? extends O>> function) {
List<O> result = new ArrayList<>();
for (I element : list) {
Optional<? extends O> optional = function.apply(element);
if (optional.isPresent()) {
result.add(optional.get());
}
}
return result;
}
The reason I used wildcard arguments for Function are as follows:
I don't really care if the input to the Function is exactly I. It should be fine to pass anything that takes a parent type of I.
The Optional that the Function returns doesn't have to be exactly O. It should be fine to return an Optional of a subtype of O.
Let's test this by first defining two simple types:
public class Animal {}
public class Cat extends Animal {}
Now suppose we're starting with a list of Cats:
List<Cat> catList = new ArrayList<>();
According to the two points I made about transform taking wildcard arguments above, I would like to transform this list into another list of Animals using the following method:
public Optional<Cat> animalToCat(Animal cat) {
return Optional.empty();
}
This indeed works when I pass a method reference to animalToCat:
List<Animal> animalList = transform(catList, this::animalToCat); // works!
However, what if I don't directly have this method reference available, and would like to store the method in a variable first before passing it to transform later?
Function<Animal, Optional<Cat>> function2 = this::animalToCat;
List<Animal> animalList2 = transform(catList, function2); // does not compile!
Why does the line resulting in animalList compile, but the one resulting in animalList2 doesn't? Through trial and error, I figured out that I can indeed make this compile by changing the type of the variable I'm assigning my method reference to to any of the following:
Function<Animal, Optional<? extends Animal>> function3 = this::animalToCat;
List<Animal> animalList3 = transform(catList, function3); // works
Function<Cat, Optional<? extends Animal>> function4 = this::animalToCat;
List<Animal> animalList4 = transform(catList, function4); // works
Function<? super Cat, Optional<? extends Animal>> function5 = this::animalToCat;
List<Animal> animalList5 = transform(catList, function5); // works
So it seems to be okay to assign the animalToCat method reference, which is clearly one taking an Animal and returning a Optional<Cat>, to other Function types. However, taking the existing animalToCatFunction2 and assigning it to the other types also fails:
animalToCatFunction3 = animalToCatFunction2; // does not compile!
animalToCatFunction4 = animalToCatFunction2; // does not compile!
animalToCatFunction5 = animalToCatFunction2; // does not compile!
I'm very confused as to why it would be okay to treat the this::animalToCat method reference in a way that would make it seem like the returned Optional<Cat> is a covariant type, but that behavior suddenly breaks as soon as the reference is assigned to a variable with a specific type. Is my definition of transform wrong?
See STU's reply for the underlying reason.
To get your example to work, you need to help the Java type system a bit by using more type parameters:
<A, B, C extends B, D extends A> List<B> transform(List<D> list, Function<A, Optional<C>> function) {
List<B> result = new ArrayList<>();
for (D element : list) {
Optional<C> optional = function.apply(element);
if (optional.isPresent()) {
result.add(optional.get());
}
}
return result;
}
Since the dynamic sub-type of the incoming list can be different to the outgoing one, it's useful to add another sub-type to the example:
class Dog extends Animal {}
Then the example code is:
List<Dog> dogList = new ArrayList<>();
List<Animal> animalList = transform(dogList, this::animalToCat);
Function<Animal, Optional<Cat>> function2 = this::animalToCat;
List<Animal> animalList2 = transform(dogList, function2);
EDIT: Modified the type constraints a bit to make it work with the example in the comment as well:
Optional<Animal> catToAnimal(Cat cat) {
return Optional.empty();
}
Function<Cat, Optional<Animal>> function2b = this::catToAnimal;
List<Animal> animalList2b = transform(catList, function2b);
I believe the reason this does not compile:
Function<Animal, Optional<Cat>> function2 = this::animalToCat;
List<Animal> animalList2 = transform(catList, function2);
whereas this does:
List<Animal> animalList = transform(catList, this::animalToCat);
is that bare method references have a type that is not representable in the user-visible type system.
You can think of this::animalToCat as having the "magic" type Function<contravariant Animal, covariant Optional<Cat>>, but as soon as you convert to the user-defined type Function<Animal, Optional<Cat>> you lose the information about the variance of raw functions themselves.
At that point, you can't assign a Function<Animal, Optional<Cat>> to a Function<Animal, Optional<? extends Animal>> for the same reason you can't assign a Function<Optional<Cat>, Animal>to a Function<Optional<? extends Animal>, Animal>.
C.f. Function1[-T1, +R] in Scala.
class Animal{
public void findAnimal(){
System.out.println("Animal class");
}
public void sayBye(){
System.out.println("Good bye");
}
}
class Dog extends Animal{
public void findAnimal(){
System.out.println("Dog class");
}
}
Given the inheritance above ,it is understood that a reference of Animal can refer to an object of Dog
Animal animal=new Dog();
As a Dog object can perform everything an Animal can do like in above case a Dog also have sayBye and findAnimal methods.
But why it is allowed to downcast an Animal object to a Dog object which serves no purpose and fails at runtime.
Dog dog=(Dog)new Animal(); // fails at runtime but complies.
Dog dog=(Dog)animal;
The above statement look logical as the animal reference is pointing to a Dog object.
This sort of casting is allowed for situations when you get an object of a superclass from outside code, e.g. as a parameter to your method, and then you need to call methods specific to a subclass.
This is not a good practice, but in some rare situations you are forced to do things like that, so the language allows it:
void sound(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog)animal();
dog.bark();
}
if (animal instanceof Cat) {
Cat cat = (Cat)animal();
cat.meow();
}
}
why it is allowed to compile Dog dog=(Dog) new Animal()
Because compiler designers decided to not detect this error at compile time. They verified that the expression being cast to Dog is of type that is a superclass of Dog, and allowed the expression to compile. They could go further and check that the expression will always result in an exception, but that would require an additional effort for very little improvement in user experience with the language.
Because you need it sometimes.
Especially when Java did not yet have generics (Java 1.4 and older), you almost always needed to cast when you got for example an object out of a collection.
// No generics, you don't know what kinds of objects are in this list
List list = new ArrayList();
list.add(new Dog());
// Need to cast because the return type of list.get() is Object
Dog dog = (Dog)list.get(0);
Since we have generics since Java 5, the need for casting is greatly reduced.
You should try to avoid casting in your code as much as possible. A cast is a way to deliberately switch off the compiler's type checking - in general you don't want to do that, you want to make use of the compiler's checking instead of circumventing it. So, if you have code where you need to cast, think a bit further to see if you can write it without the cast.
You need that capability to access an earlier cast object as its original type.
For example, if you cast a Dog to an Animal to pass it to a generic processor, you may later need to cast it back to a Dog to perform specific methods.
The developer is responsible to make sure the type is compatible - and when it is there will be no error. Some pseudo code:
public void example(Animal foo){
if( ...condition... ) ((Dog)foo).bark();
else if( ...other condition... ) ((Cat)foo).meow();
}
Since the introduction of generics, this is less commonly used, but there are still cases for it. The developer is solely responsible for guaranteeing the type is right if you don't want an error.
case 1 -
Here we use loose coupling.
Animal animal = getSomeDog(),
Dog dog = (Dog) animal; // this is allowed because animal could reference a dog
case 2
Here we you use tight coupling.
Animal animal = new Animal();
Dog dog = (Dog) animal; // this will fail at runtime, because animal doesn't reference a Dog
We use Downcasting when there is possibility to succeed at run time
so case 1 has possibility to succeed at runtime over case 2
Down casting is considered as a bad Object Oriented practice. It must be avoided to as much extent as possible.
Java still has it and your question is a good question as why Java allows Down-casting.
Suppose a case below.
public interface List{
public boolean add(Object e);
public boolean remove(Object o);
}
public class ArrayList implements List{
// Extra method present in the ArrayList and not in the parent Interface
public Object[] toArray() {
// returns array of the objects
return Arrays.copyOf(elementData, size);
}
#Override
public boolean add(Object e){
// add e to the ArrayList Underlying array
}
#Override
public boolean remove(Object o){
// remove o from the ArrayList Underlying array
}
}
A good Object oriented practice is to Code for Interfaces. But often there are methods defined in the concrete implementations which we need to call. I read an comment from some one and I quote it in my words.
Know the Rules, in case you need to break them Do break them Knowingly and take care so as to prevent from any adverse effect.
Below is an example where we need to do the Down-casting. The example of down-casting in your question is to teach what is down-casting, below is real life example.
public void processList(List items){
items.add( new Object() );
items.add( new Object() );
processAsPerTypeOfList(items);
}
public void processAsPerTypeOfList( List items ){
if( items instanceof ArrayList){
Object[] itemArray = ((ArrayList)items).toArray();// DOWNCASTING
// Process itemArray
}
}
For more reference you can also see a related question : Why Java needs explicit downcasting?
There are 2 classes A and B, B extends A. What is the difference between
A a = new B();
and
B b = new B()?
Both create the object of class B. What is the difference?
You are right that in both cases an object of class B is created. The difference between the two declarations is in the type of the variable.
It is very important to keep the distinction between variables and objects in mind. For example, the following code defines 3 variables but only 2 objects:
Circle c1 = new Circle(5);
Circle c2 = c1;
Circle c3 = new Circle(5);
When you say
Shape s = new Circle(5);
instead of
Circle s = new Circle(5);
assuming Circle extends Shape then, even though in both cases you did create a circle object, in the former case you can only call shape methods on the circle (through the variable s) whereas in the second case you can you all circle methods (because you will be calling them through the circle variable c). That is a call like s.getArea() will work in both cases but something like s.getRadius() will ONLY be allowed in the second (unless you use an ugly cast).
So why do we often do things like the first case? That is, why do we often define our variables of a more general type than necessary? Usually we do this because we want to restrict the interface for safety. Perhaps we only care about shapes, but in this case the particular shape just happens to be a circle. If you cared about circle specific properties, then we would have used a circle variable. But we should strive to be as general as possible. Coding to the most general interface allows our code to work with shapes other than circles without modification.
Of course, for this to really sink in, you have to experience it firsthand, but hopefully this explanation is a start. There are many books and blog posts and articles that explain this in more detail with useful real-life anecdotes I'm sure.
A a = new B();
has only the attributes and methods of A.
B b = new B();
has the the attributes and methods of B.
If you added some attributes or methods to B, you can't call them with a.
The advantage is
Fruit f = new Mango();
Suppose
consumeFruit(Fruit f);
now you can call
consumeFruit(new Mango());
consumeFruit(new Strawberry());
Note:
For this case you would be only able to call the methods declared in the reference type. and object type's version will get invoked . and you would be only accessing fields from the reference type's class
See Also
Liskov substitution principle
If you say
List a = new ArrayList();
then you reference ArrayList only in one place in your code. That makes it easier to change it later to something else, like LinkedList;
Of course, this does not work if you need methods specific to ArrayList.
In general, you should use the most general type applicable.
This question is on Polymorphism. Following is an extract from Kathy Siera:
public class TestAnimals {
public static void main (String [] args) {
Animal a = new Animal();
Animal b = new Horse(); //Animal ref, but a Horse object
a.eat(); // Runs the Animal version of eat()
b.eat(); // Runs the Horse version of eat()
}
}
class Animal {
public void eat() {
System.out.println("Generic Animal Eating Generically");
}
}
class Horse extends Animal {
private void eat() { // whoa! - it's private!
System.out.println("Horse eating hay, oats, "
+ "and horse treats");
}
}
If this code compiled (which it doesn't), the following would fail at runtime:
Animal b = new Horse(); // Animal ref, but a Horse
// object , so far so good
b.eat(); // Meltdown at runtime!
Suppose this example:
We have class an animal:
public class Animal {
public void eat() {
// each animal can eat
}
}
Now we have another class dog:
public class Dog extends Animal {
public void bark() {
// dogs can bark
}
}
Now we can write this code:
Animal pet = new Dog();
Now we know, that pet can eat, but nothing more. But if we write
Dog pet = new Dog();
Then we know, that our pet can eat and bark.
Also there is safe and unsafe casting. Safe casting is from Dog to an Animal because each dog is animal (extends it)
Dog pet = new Dog();
Animal animal = pet;
But if we want to cast Animal to Dog we have to test if the instance of animal is really dog, because it doesn't have to be.
Animal pet = new Dog();
Dog myDog = null;
if (pet instanceof Dog) {
myDog = (Dog) pet;
}
Usually, declaring a parent class and assigning it an inherited class is useful when the parent class variable may be assigned different objects. For example
Pet p;
if (favoritePet == Pets.CAT) {
p = new Cat();
} else {
p = new Dog();
}
System.out.println(p.someMethodFromPet());
I'm sure this is incredibly common with as OOP centered as Java is. In java is there a way to make a base type variable that accepts all inherited subtypes? Like if I have;
class Mammal {...}
class Dog extends Mammal {...}
class Cat extends Mammal {...}
class ABC {
private Mammal x;
ABC() {
this.x = new Dog();
-or-
this.x = new Cat();
}
}
I need the variable to be able to accept any extended version too, but not in specific one extended kind.
There are some ways that I know, but don't want to use. I could make an attribute for each subtype, then only have the one attribute actually used. Make an array and shove it in there.
Any other ideas or a way to get a "base class" type variable?
Ok since I know using polymorphic duck typing isn't a great idea in Java, but since I don't think I can avoid it. Is the only way to use subclass methods dynamically to re assign a casted version of the varible ie, I get an error with this;
Mammal x;
x = new Dog();
System.out.println(x.getClass());
x.breath();
if (x instanceof Dog) {
x.bark();
} else if (x instanceof Cat) {
x.meow();
}
Saying symbol not found, however this works;
Mammal x;
x = new Dog();
System.out.println(x.getClass());
x.breath();
if (x instanceof Dog) {
Dog d = (Dog) x;
d.bark();
} else if (x instanceof Cat) {
Cat c = (Cat) x;
c.meow();
}
That last one the only way to do it?
If you have the following:
class Mammal {...}
class Dog extends Mammal {...}
class Cat extends Mammal {...}
Then Dog is a subtype of Mammal. Cat is also a subtype of Mammal. This type polymorphism does in fact allow you to do the following:
Mammal x;
x = new Dog(); // fine!
x = new Cat(); // also fine!
If in fact later there's the following:
class Platypus extends Mammal {...} // yes it's true!
Then you can also do:
x = new Platypus(); // fine!
This polymorphic subtyping relationship is one of the basic tenets of object-oriented programming.
See also
Java Tutorials/Object-Oriented Programming Concepts
Wikipedia/Polymorphism in object-oriented programming
Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented programming, is the ability of one type, A, to appear as and be used like another type, B
On instanceof type comparison operator
Suppose we have the following:
class Mammal { void lactate(); }
class Dog extends Mammal { void bark(); }
class Cat extends Mammal { void meow(); }
Then you can use instanceof type comparison operator (ยง15.20.2) to do something like this:
Mammal x = ...;
if (x instanceof Dog) {
Dog d = (Dog) x;
d.bark();
} else if (x instanceof Cat) {
Cat c = (Cat) x;
c.meow();
}
if (x != null) {
x.lactate();
}
There are also ways to do this without if-else; this is just given as a simple example.
Note that with appropriate design, you may be able to avoid some of these kinds of subtype differentiation logic. If Mammal has a makeSomeNoise() method, for example, you can simply call x.makeSomeNoise().
Related questions
When should I use the Visitor Design Pattern? - sometimes used to simulate double dispatch
On reflection
If you must deal with new types not known at compile-time, then you can resort to reflection. Note that for general applications, there are almost always much better alternatives than reflection.
See also
Java Technical Articles/Advanced Language Topics/Reflection
Effective Java 2nd Edition, Item 53: Prefer interfaces to reflection