简体   繁体   中英

How to avoid creating the same object reference multiple times?

Imagine that I have some classes that looks like this:

class Car {
    private Image carImage;

    public Car(int imageIndex) {
        switch (imageIndex) {
            case 1: carImage = generateCarImage(1); break;
            # and so forth
        }
    }
}

class Audi extends Car {
    private int numberOfSeats;
    public Audi(int imageIndex, int numberOfSeats) {
        super(imageIndex);
        this.numberOfSeats = numberOfSeats;
    }
}

Now imagine that I create multiple Audi's using the same image:

Audi car1 = new Audi(1,2);
Audi car2 = new Audi(1,3);

Will car1 and car2 extend the same object? I assume not, but is there a way I can make it so? I'm asking because I want to avoid generating and storing the same image twice.

EDIT:

Will these two audi's reference the same car, eg the image is generated and stored only once, and any changes to one affects the other?

class Car {
    private Image carImage;
    public Car(int imageIndex) {
        switch (imageIndex) {
            case 1: # carImage = readfile(1.jpeg)
            # and so forth
        }
    }
}

class Audi{
    private int numberOfSeats;
    private Car car;
    public Audi(Car car, int numberOfSeats) {
        this.car = car;
        this.numberOfSeats = numberOfSeats;
    }
}

Car car = new Car(1);
Audi audi1 = new Audi(car,2);
Audi audi2 = new Audi(car,2);

EDIT 2:

There are a lot of good answers here, and I ended up using a combination of them to create a decent solution. My initial problem was not very well defined, mainly because I didn't know myself exactly what it was.

Anyway, for this problem it is not possible to generate all the data ( PartsInfo in the example below) beforehand, nor can I generate the data explicitly (as implied by the switch-case example above). The biggest problem with the solution below is that I can't access individual fields in PartsInfo without retrieving the whole thing (as is done in the solution when Car.getPartsInfo() is called) or creating multiple instances of the same object (in which case the Car class would get its own PartsInfo variable).

A weak hashmap would also do, but not optimal because the problem is not garbage collection, but huge amount of identical data stored in separate instances.

The solution is applicable if the ID is something like "audi-a4-2003" and PartsInfo is identical for all "audi-a4-2003" independent of color, owner, age, number of seats etc, but completely different for "audi-a4-2004".

Thanks

Class PartsInfo {
    // lots of stuff I'd rather not create nor save multiple times
}

Class PartsInfoFactory {
    private static HashMap<String, PartsInfo> partsInfoMap = new HashMap<String, PartsInfo>();

    public static getPartsInfo(String id) {
        if (!partsInfoMap.containsKey(id)) {
            generatePartsInfo(id);
        }
        return partsInfoMap(id)
    }

    private static generatePartsInfo(String id) {
        // Do stuff I don't want to do twice for same ID
        partsInfoMap.put(id)
    }
}

Class Car {
    private Color color;
    private String id;
    // Notice that PartsInfo is not stored here

    public Car(Color color, String id) {
        this.color = color;
        this.id = id;
    }

    public PartsInfo getPartsInfo() {
        return PartsInfoFactory.getPartsInfo(id);
    }
}

Will car1 and car2 extend the same object?

A class can extend from another class.. Objects do not extend anything. In Java, inheritance is just for classes and interfaces. What you're doing here is creating two instances of the same class, Audi , and Audi extends from Car .

is there a way I can make it so?

No.

I'm asking because I want to avoid generating and storing the same image twice.

This is the proper question to answer. Your real problem is dealing with avoiding to create the same object instance multiple times. For this, it will be better to use an object pool by making use of a WeakHashMap . Here's an explanation on why to use this structure: When would you use a WeakHashMap or a WeakReference?

A good way to avoid creating the same image multiple times is to use dependency injection: inject the image as a constructor parameter, rather than passing in the parameter to generateCarImage :

class Car {
  private final Image image;

  Car(Image image) {
    this.image = image;
  }
}

class Audi extends Car {
  Audi(Image image, int numDoors) {
    super(image);
    // ...
  }
}

This means that image can come from anywhere - giving you more explicit control over the lifecycle of the images. So, if you want to use the same image over and over, you can, and it's obvious that you are:

Image image = generateCarImage(1);
Audi car1 = new Audi(image, 4);
Audi car2 = new Audi(image, 2);

Also, by removing static coupling to your generateCarImage method, it makes the class more testable, since you can create different images for testing, eg that are simpler to generate.

You never extend objects, you extend the class. And of course you will be extending the same class all the time.

In Java there is no such thing as extending an object (other languages have this kind of inheritance, called prototypal . However, Java does not have prototypal inheritance; only a class inheritance).

Extending in Java means extending a class, not an object, which is an instance of a class.

Therefore, although the classes of car1 and car2 extend the same class, the two objects are unrelated to each other.

I want to avoid generating and storing the same image twice

There is no problem with multiple objects sharing a third object, which in your case could be an image. One way to deal with this would be creating an image cache common to all instances of Car , generate the image the first time that it is requested, and then re-using the same image object as needed to save space:

简单的图表

Is it possible to, instead of searching a cache of images, searching through a cache of all instances of Car , and then choose which one to instantiate in the Audi class?

You cannot instantiate an object for a second time. However, you can make a cache of Car objects, and implement a factory method on the Car that searches its cache for a suitable car before making a new instance.

Everytime you're using the new clause you will be creating a new instance of the object, a complete separate representation of the class; so answering to the direct question: no, you're not extending the object.

The underlying question is that you may not want to repeat the creation of to equal images: Then you must make a different approach. I recomend first to do another read to the OO aspect of Java, then think on (maybe) the factory patter which could be a class that will take care of not repeating the creation of to equal images if another was already created.

My solution is using static references to be used as constant values. This is the easiest solution, given that enum won't work with objects, since it has to be evaluated at compile-time.

But we want to get both a run-time constants, and the benefit of using an enum like using single-instance and can be used in a switch statement.

So we are going to implement the enum to return constant static attributes of another class which is available at compile-time, and return a constant reference to an object created on run-time.

class CarImageDirectory
{
    // Created at Run-time

    public static final Image Audi = new Image("Audi");
    public static final Image Toyota = new Image("Toyota");
    // ..etc
}

enum CarImage
{
  // Created at Compile-time

  Audi
  {
    @Override public Image image () { return CarImageDirectory.Audi; }
  },
  Toyota
  {
    @Override public Image image () { return CarImageDirectory.Toyota; }
  }; // ..etc

  public abstract Image image ();
}

CarImage will work like this:

CarImage A = CarImage.Audi;
CarImage B = CarImage.Audi;

if (A == B) System.out.println("A and B are both Audi");

Then we just define our Car class using it:

class Car
{
    private CarImage carImg;
    public Car (CarImage carImg) { this.carImg = carImg; }
    public Image getImage () { return carImg.image(); }
    public CarImage getCarImage () { return carImg; }
}

class AudiCar extends Car
{
    private int numOfSeats;

    public AudiCar (int numOfSeats)
    {
        super(CarImage.Audi);
        this.numOfSeats = numOfSeats;
    }
}

class ToyotaCar extends Car
{
    private int numOfSeats;

    public ToyotaCar (int numOfSeats)
    {
        super(CarImage.Toyota);
        this.numOfSeats = numOfSeats;
    }
}

Also CarImage itself can be used in switch statement too:

CarImage A = CarImage.Audi;

switch(A)
{
  case CarImage.Audi:
    System.out.println("This car is Audi");
    break;

  case CarImage.Toyota:
    System.out.println("This car is Toyota");
    break;

  default:
}

Have you looked into "flyweight" pattern? That might reduce object creation for you. Technically, it's for reducing memory footprint, but if object creation is expensive and there is high reuse, you can use it in situations where startup time is not an issue, such as with application-server startups.

In any event only optimize if you know it's a performance problem.

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM