Problema con Class, extend y herencia

Hola a todos.

Este es mi primer post. Soy bastante nuevo en esto.
Estoy haciendo una clase, supongamos “animales”. Dentro de la misma tengo un procedimiento “void hijo”, que crea una copia de si misma con algunas variaciones y la agrega una arraylist de la cual forman parte todos los “animales”. Por otro lado tengo una clase “perro” que extiende la clase “animales”. Cuando llamo al procedimiento “hijo” de la clase “perro” me crea otro “animal” y no un perro. Por ahora lo soluciono copiando todo el procedimiento “hijo” en la subclase y cambiando de “Animal nuevo = New Animal;” por “Perro nuevo = New Perro”. Estoy seguro que tiene que haber una mejor manera. Si alguien me puede ayudar se lo agradecería.

Saludos!

1 Like

Disclaimer: I know English & Portuguese only. :expressionless:

And although I can understand Spanish more or less, still I can’t properly reply using it, sorry. :disappointed:

Anyways, we can always count on Google Translate, right? :relieved:

Indeed, if your method Animal::hijo() is something like this:

class Animal {
  Animal hijo(final String name) {
    return new Animal(name);
  }
}

And you’ve got a class that extends it:

class Perro extends Animal {
  Perro(final String name) {
    super(name);
  }
}

Java won’t “automagically” upgrade the datatype Animal in return new Animal(name); to the datatype of that subclass like return new Perro(name); in any way! :cry:

As a workaround, we could use Java reflection techniques in order to always instantiate the current subclass’ Constructor: :building_construction:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/Constructor.html

However, reflection is complicated! And thus it should be used only if there’s no other alternative: :warning:

And a simpler alternative is to implement the Cloneable interface and its clone() method: :bulb:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Cloneable.html
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#clone()

Here’s an example of a class named Pair which implements Cloneable: :nerd_face:

And inspired by my previous sketch above, here’s “Cloneable Inheritance”: :cowboy_hat_face:

/**
 * Cloneable Inheritance (v1.0.1)
 * GoToLoop (2019/May/13)
 * Discourse.Processing.org/t/problema-con-class-extend-y-herencia/11204/2
 */

import java.util.List;
final List<Animal> animales = new ArrayList<Animal>();

void setup() {
  animales.add(new Animal("Madre"));
  println(animales);

  animales.add(animales.get(animales.size() - 1).hijo("Hijo"));
  println(animales);

  animales.add(new Perro("Rex"));
  println(animales);

  animales.add(animales.get(animales.size() - 1).hijo("Perla"));
  println(animales);

  exit();
}

class Animal implements Cloneable {
  String name;

  Animal() {
  }

  Animal(final String name) {
    set(name);
  }

  Animal set(final String nick) {
    name = nick;
    return this;
  }

  Animal hijo(final String name) {
    //return new Animal(name);
    return clone().set(name);
  }

  @Override Animal clone() {
    try {
      return (Animal) super.clone();
    }
    catch (final CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }

  @Override String toString() {
    return name + " el " + getClass().getSimpleName();
  }
}

class Perro extends Animal {
  Perro() {
  }

  Perro(final String name) {
    super(name);
  }
}
2 Likes

I really appreciate your response. I thanks you for taking the job of translating my question. I understand english! I´m not so good talking it, or writing it. I´m trying to understand your response, and din´t do it yet! I don´t know how to publish code in the right format, but more or less this is what i’m doing. I would spect to have:

Ball
Subclass
Ball
Subclass

but I get:

Ball
Subclass
Ball
Ball

    /**
     * 
     * 
     * aledante@hotmail.com
     */
    import org.gicentre.utils.geom.*; 
    import java.util.*;
    HashGrid<Ball> balls;
    Set<Ball> paranacer=new HashSet<Ball>();
    
    void setup() {
      size(400, 400);
    
      balls = new HashGrid(width, height, 10);
    
      balls.add(new Ball());
      // println(balls);
      balls.add(new Subclass());
      //  println(balls);
    }
    
    void draw() {
      
      // I add all to a set.
      for (Iterator<Ball> iterator = balls.iterator(); iterator.hasNext(); ) {
        Ball ball = iterator.next();
        paranacer.add(ball);
      }
      // I make to execute hijo on all the ser.
      for (Iterator<Ball> iterator = paranacer.iterator(); iterator.hasNext(); ) {
        Ball ball = iterator.next();
        ball.hijo();
      }
      for (Iterator<Ball> iterator = balls.iterator(); iterator.hasNext(); ) {
        Ball ball = iterator.next();
        println(ball.nombre);
      }
    
      exit();
    }
    
    class Ball implements Locatable {
      PVector location; 
      String nombre;
    
      Ball() {
        location = new PVector(40, 20);
        nombre ="Ball";
      }
      void hijo() {
        Ball hijo = new Ball();
        balls.add(hijo);
      }
    
      public PVector getLocation()
      {
        return location;
      }
    }
    
    class Subclass extends Ball {
      Subclass() {
        super();
        nombre="Subclass";
      }
    }
1 Like

I didn’t need to translate anything, nor use Google Translate. :wink:

Like I had already explained, you’re gonna need to implement clone() in your own class, rather than relying on the new operator:

So instead of:

Ball hijo() {
  return new Ball();
}

Go w/ something like this:

Ball hijo() {
  return clone();
}

@Override Ball clone() {
  try {
    final Ball hijo = (Ball) super.clone();
    hijo.location = location.get();
    return hijo;
  }
  catch (final CloneNotSupportedException e) {
    throw new RuntimeException(e);
  }
}

Notice I had to do: hijo.location = location.get();. Here’s the reason why: :warning:

  • Your class Ball got 2 fields: location & nombre.
  • The 1st refers to a PVector, while the 2nd refers to a String.
  • Datatype String is immutable. Its content can’t be changed (at least not w/o some hardcore hacking :smiling_imp:).
  • However, datatype PVector is mutable. Its fields x, y & z can be freely reassigned.
  • If we don’t also clone each mutable field from a class when we call clone(), modifying those mutable fields will reflect on both the original object and all of its clones! :fearful:
  • That’s why I invoke the method PVector::get() over the cloned field location, so the original and the cloned field location won’t share the same PVector object: :nerd_face:
  • ProcessingJS.org/reference/PVector_get_/

Below’s my new attempt sketch “Cloneable Locatable”. Any doubts about it just ask: :innocent:

/**
 * Cloneable Locatable (v1.0.1)
 * GoToLoop (2019/May/14)
 * Discourse.Processing.org/t/problema-con-class-extend-y-herencia/11204/4
 */

import org.gicentre.utils.geom.HashGrid;
import org.gicentre.utils.geom.Locatable;

import java.util.Collection;
//import java.util.Set;

static final int SIZE = 10;
final Collection<Bola> temp = new ArrayList<Bola>();

Collection<Bola> pelotas;
//Set<Bola> pelotas;
//HashGrid<Bola> pelotas;

void setup() {
  size(400, 400);
  noLoop();

  pelotas = new HashGrid<Bola>(width, height, SIZE);

  pelotas.add(new Bola());
  pelotas.add(new Bolita());
}

void draw() {
  background((color) random(#000000));
  getSurface().setTitle("Frame: " + frameCount);
  println("\nPelotas: " + pelotas.size());
  for (final Bola b : pelotas)  println(b);
}

void mousePressed() {
  temp.clear();
  for (final Bola b : pelotas)  temp.add(b.hijo());
  pelotas.addAll(temp);
  redraw = true;
}

class Bola implements Locatable, Cloneable {
  PVector location = new PVector(40, 20);
  String nombre = getClass().getSimpleName();

  Bola hijo() {
    return clone();
  }

  @Override Bola clone() {
    try {
      final Bola pelota = (Bola) super.clone();
      pelota.location = location.get();
      return pelota;
    }
    catch (final CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }

  @Override PVector getLocation() {
    return location;
  }

  @Override String toString() {
    return nombre + ": " + location;
  }
}

class Bolita extends Bola {
}
1 Like

Thanks again. Great answer, extremely complete, and clear.
Some questions:

  1. I made the example code very simple to explain my problem. My class have several variables. Some int, another PVector, some float, and another class. Only PVector and class are mutables, isn´t it?

So

  @Override Bola clone() {
    try {
      final Bola pelota = (Bola) super.clone();
      pelota.location = location.get();
pelota.vel = location.get(); // This is the other PVector
pelota.comp=comp.copia(); // This is a method that I have to make to copy my other class
      return pelota;
    }

Is it correct?

  1. Is it necessary to use “@Override”. In Processing reference says that is not… But I see it a lot. :face_with_monocle:

TXH!:grin:

Ehhh… And why you do this:

  @Override PVector getLocation() {

    return location;
  }

@Override’s just a labeling annotation. You can remove them all: :wave:
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Override.html

In general the cloning assignment should use the same field: :copyright:
pelota.vel = vel.get();

However, if your intention is indeed to give some other initial value to a newborn cloned field, that’s OK AFAIK. :baby:

Only you know better whether the cloned field Bola::vel should be born using its old value or a diff. 1. :mage:

1 Like

You are my hero!
Thanks😀

Last little question. Is color mutable or inmutable? THX

  • Processing’s color is syntactic sugar for Java’s int keyword.
  • Java got 8 primitive datatypes represented by 8 keywords.
  • All primitive types are immutable. In Java they’re not even objects.

Hello again,
Excuse me. But i can not get ir right.
In the extended class I want to make some changes over child objects.
Why i cannot invoke clone method on extended class? Why I cannot invoke super class?
I show the code you send me with the modifications i would like to do, that shows me mismatch errors.
The problem is in the last few lines.

/**
 * Cloneable Locatable (v1.0)
 * GoToLoop (2019/Jan/) 
 * Discourse.Processing.org/t/problema-con-class-extend-y-herencia/11204/4 
 */

import org.gicentre.utils.geom.HashGrid;
import org.gicentre.utils.geom.Locatable;
import java.util.List;
import java.util.Set;
static final int SIZE = 10;
final List<Bola> temp = new ArrayList<Bola>();

Set<Bola> pelotas;

// HashGrid<Bola> pelotas;
void setup() {
  size(400, 400);
  noLoop();
  pelotas = new HashGrid<Bola>(width, height, SIZE);
  pelotas.add(new Bola());
  pelotas.add(new Bolita());
}

void draw() {

  background((color) random(#000000));
  getSurface().setTitle("Frame: " + frameCount);
  println(ENTER + "Pelotas: " + pelotas.size());
  for (final Bola b : pelotas)  println(b);
}

void mousePressed() {

  temp.clear();
  for (final Bola b : pelotas)  temp.add(b.hijo());
  pelotas.addAll(temp);
  redraw = true;
}
class Bola implements Locatable, Cloneable {
  PVector location = new PVector(40, 20);
  String nombre = getClass().getSimpleName();

  Bola hijo() {
    Bola hijo=clone();
    return hijo;
  }

  @Override Bola clone() {

    try {

      final Bola pelota = (Bola) super.clone();
      pelota.location = location.get();
      return pelota;
    }
    catch (final CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }

  @Override PVector getLocation() {
    return location;
  }

  @Override String toString() {
    return nombre + ": " + location;
  }
}

class Bolita extends Bola {
  Bolita hijo() {
    Bolita hijo=super.hijo();
    hijo.nombre=hijo.nombre+"2";
    return hijo;
  }
  Bolita tween() {
    return clone();
  }
}

  • Obviously, method Bola::hijo() returns an object of datatype Bola.
  • And it can’t be converted to its derived datatype Bolita.
  • However, it doesn’t need to, b/c the inherited field nombre exists in datatype Bola too:
class Bolita extends Bola {
  @Override Bola hijo() {
    final Bola hijo = super.hijo();
    hijo.nombre += '2';
    return hijo;
  }
}
  • However, in case subclass Bolita would add its own fields, besides those it had inherited from Bola, you’d probably need to @Override Bola::clone() in subclass Bolita, if any of those new fields happen to be mutable.

You are like my Super Hero! :grin: