2

I am currently teaching myself java, and one of the practice problem's final output needs different name for different objects (which is decided based on random number generator). However, every time I create object, those object ends up having same name. To give a clear example, following is the sample output:

Expected Output

and following is my output:

enter image description here

As highlighted in my output, all of my animals have same name while the expected output have different names. I've tested multiple times and can confirm that this is not due to some luck.

My code for Cow class, which constructs name & other information, is following (I've omitted irrelevant methods):

import java.lang.Math;
import java.util.Random;

public class Cow implements Milkable, Alive {
    private String name;
    private double capacity;
    private double amount;

    private static final String[] NAMES = new String[]{
        "Anu", "Arpa", "Essi", "Heluna", "Hely",
        "Hento", "Hilke", "Hilsu", "Hymy", "Ihq", "Ilme", "Ilo",
        "Jaana", "Jami", "Jatta", "Laku", "Liekki",
        "Mainikki", "Mella", "Mimmi", "Naatti",
        "Nina", "Nyytti", "Papu", "Pullukka", "Pulu",
        "Rima", "Soma", "Sylkki", "Valpu", "Virpi"};

    private static final String randName = NAMES[new Random().nextInt(NAMES.length)];

    // Default Constructor <- where issue is
    public Cow(){
        this(randName);
    }

    // Overloaded Constructor
    public Cow(String name){
        this.name = name;   
        this.capacity = (15.0 + new Random().nextInt(26)); 
        this.amount = 0.0;
    }

    // accessors...
    // milk... removes milk from cow's tank (implements Milkable interface)
    // liveHour... adds milk to cow's tank (implements Alive interface)
    // toString...
}

In addition, this is my main class that uses above (again, omitted unnecessary parts):

        Farm farm = new Farm("Esko", new Barn(new BulkTank()));

        farm.addCow(new Cow());
        farm.addCow(new Cow());
        farm.addCow(new Cow());

        System.out.println(farm);

This post indicates that random number generator ends up being deterministic if there is a seed already set on constructor. However, my random number generator has to have a specific seed (length of array containing random names in this case) in order to choose random name. I was wondering if anyone knows a way around to make my random number generator to produce new values. Thank you in advance.

selfPointer
  • 339
  • 1
  • 2
  • 12
  • I mean, why do you need to create a new instance of the random object everytime? It should be uniformedly distributed and non-deterministic by just having one seed. – Ssr1369 Nov 14 '19 at 10:37
  • It's not a strict requirement to have consecutive random names, but I was curious on how to get random name per each run. For clarity, name itself becomes randomized after each run, just not for all three. – selfPointer Nov 14 '19 at 10:42
  • Yes I get it, I think the solution Hotzst provided should work. But I'd probably create a single Random object as opposite to the new Random you're doing everytime. I don't think that affects any statistical metric of its result, but I consider a good practice to not do that. Although tbf I don't know if it actually is or not. – Ssr1369 Nov 14 '19 at 10:45

3 Answers3

8

The problem is this line:

private static final String randName = NAMES[new Random().nextInt(NAMES.length)];

You define the randName as a static variable, that is bound to the class and not the instance, meaning that every instance of Cow has the same randName.

In your default constructor, you should have:

public Cow(){
    this(NAMES[new Random().nextInt(NAMES.length)]);
}
hotzst
  • 7,238
  • 9
  • 41
  • 64
  • Thank you, this worked. I was bit surprised since I did something similar in the beginning but wouldn't compile, but that was because I declared ```randName``` within default constructor (hence I used ```private static later on```). It seems that directly initializing random name within ```this()``` actually works. Again, thank you for your help. – selfPointer Nov 14 '19 at 10:54
1

randName is declared as static which might be causing the problem. static fields get loaded only once at class loading time. Try declaring it as an instance variable (non-static)

Gaurav Dhiman
  • 953
  • 6
  • 11
1

In addition of hotzst's answer, there is no reason to create a new Random instance for each instance of Cow you create.

For example, you can define a name generator as follows:

private static final Random random = new Random();
private static final Supplier<String> namesGenerator = () -> NAMES[random.nextInt(NAMES.length)];

Now you can instantiate a Cow with:

public Cow() {
    this(namesGenerator.get());
}
Eran
  • 387,369
  • 54
  • 702
  • 768