'How to use generics in a simple java tree structure?
I'm learning Java generics/inheritance and now I'm trying to implement a Forest structure having two types of trees: coniferous with needles and deciduous with leaves. Trees can grow... It means that if a tree is growing, the trunk is getting higher, branches are added and needles/leaves are added to branches depending on a tree type. Firstly I started with general Tree Class with treeGrow method:
public class Tree<T extends TreeType> {
protected int id;
protected String name;
protected int age;
protected Trunk trunk;
protected T type;
public Tree(int id, String name, int age, Trunk trunk, T type) {
this.id = id;
this.name = name;
this.age = age;
this.trunk = trunk;
this.type = type;
}
public void treeGrow(){
this.age ++;
trunk.treeGrow();
}
then I implemented trunk which has a set of branches:
public class Trunk{
private int height;
private int width;
private Set<Branch> branches;
public void treeGrow(){
trunkGrow();
branches.forEach(Branch::treeGrow);
branches.add(new Branch(1, new ArrayList<>()));
}
private void trunkGrow(){
this.width ++;
this.height += 15;
}
}
... and a branch class:
public class Branch{
int branchLength;
List<TreeType> coates;
public void treeGrow(){
branchGrow();
coates.forEach(TreeType::coatingGrow);
TreeType leaf = new Leaf(1);
coates.add(leaf);
//here I have hardcoded Leaf adding, I want to make a Leaf/Needle choice dependent on a Tree type using T...
}
private void branchGrow(){
this.branchLength++;
}
}
with Needle/Leaf classes which implement TreeType interface
public class Needle implements TreeType {
int needleLength;
public void coatingGrow() {
this.needleLength++;
}
}
Question: How should my Branch class know whether Needle or Leaf should be added? I try to use generics, specifying the type of Tree class (extending TreeType interface which is implemented by Needle and Leaf classes), but I think I do not understand it quite right.
Solution 1:[1]
Question: How should my Branch class know whether Needle or Leaf should be added?
This sounds like an ideal situation to use a Supplier<T>.
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public interface SOQ_20220505_Attempt2
{
public final class FlatLeaf extends Leaf {
public FlatLeaf(int length) {
super(length);
}
}
public final class NeedleLeaf extends Leaf {
public NeedleLeaf(int length) {
super(length);
}
}
public sealed class Leaf{
private int length;
public Leaf(int length) {
this.length = length;
}
}
public class Branch<L extends Leaf> {
private int length;
private List<Branch<L>> subBranches;
private List<L> leaves;
private Supplier<L> leafGenerator;
public Branch(int length, List<Branch<L>> subBranches, List<L> leaves, Supplier<L> leafGenerator) {
this.length = length;
this.subBranches = subBranches;
this.leaves = leaves;
this.leafGenerator = leafGenerator;
}
public void grow() {
this.length++;
this.subBranches.forEach(Branch::grow);
this.subBranches.add(new Branch<>(1, new ArrayList<>(), new ArrayList<>(), this.leafGenerator));
this.leaves.add(leafGenerator.get());
}
}
public class Trunk<L extends Leaf> {
private int height;
private int width;
private Set<Branch<L>> branches;
private Supplier<L> leafGenerator;
public Trunk(int height, int width, Set<Branch<L>> branches, Supplier<L> leafGenerator) {
this.height = height;
this.width = width;
this.branches = branches;
this.leafGenerator = leafGenerator;
}
public void grow() {
this.width++;
this.height += 15;
this.branches.forEach(Branch::grow);
this.branches.add(
new Branch<L>(
1,
new ArrayList<>(),
new ArrayList<>(),
this.leafGenerator));
}
}
public class Tree<L extends Leaf>
{
private int id;
private String name;
private int age;
private Trunk<L> trunk;
private Supplier<L> leafGenerator;
public Tree(int id, String name, int age, Trunk<L> trunk, Supplier<L> leafGenerator) {
this.id = id;
this.name = name;
this.age = age;
this.trunk = trunk;
this.leafGenerator = leafGenerator;
}
public void grow() {
this.age++;
this.trunk.grow();
}
}
}
Below, I have reworked your program slightly, and added a runnable example.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public interface SOQ_20220505_1
{
public non-sealed class FlatLeaf extends Leaf {
public FlatLeaf(int length) {
super(length);
}
}
public non-sealed class NeedleLeaf extends Leaf {
public NeedleLeaf(int length) {
super(length);
}
}
public sealed class Leaf{
private int length;
public Leaf(int length) {
this.length = length;
}
public void grow() {
this.length++;
}
@Override
public String toString() {
return "\n\t\t\t\tLeaf [length=" + length + "]";
}
}
public class Branch<L extends Leaf> {
private int length;
private List<Branch<L>> subBranches;
private List<L> leaves;
private Supplier<L> leafGenerator;
public Branch(int length, List<Branch<L>> subBranches, List<L> leaves, Supplier<L> leafGenerator) {
this.length = length;
this.subBranches = subBranches;
this.leaves = leaves;
this.leafGenerator = leafGenerator;
}
public void grow() {
this.length++;
this.subBranches.forEach(Branch::grow);
this.subBranches.add(new Branch<>(1, new ArrayList<>(), new ArrayList<>(), this.leafGenerator));
this.leaves.add(leafGenerator.get());
this.leaves.forEach(Leaf::grow);
}
@Override
public String toString() {
return "\n\t\t\tBranch [length=" + length + ", subBranches=" + subBranches + ", leaves=" + leaves
+ ", leafGenerator=" + leafGenerator + "]";
}
}
public class Trunk<L extends Leaf> {
private int height;
private int width;
private Set<Branch<L>> branches;
private Supplier<L> leafGenerator;
public Trunk(int height, int width, Set<Branch<L>> branches, Supplier<L> leafGenerator) {
this.height = height;
this.width = width;
this.branches = branches;
this.leafGenerator = leafGenerator;
}
public Trunk<L> with(Supplier<L> leafGenerator) {
return new Trunk<>(this.height, this.width, this.branches, leafGenerator);
}
public void grow() {
this.width++;
this.height += 15;
this.branches.forEach(Branch::grow);
this.branches.add(
new Branch<L>(
1,
new ArrayList<>(),
new ArrayList<>(),
this.leafGenerator));
}
@Override
public String toString() {
return "\n\t\tTrunk [height=" + height + ", width=" + width + ", branches=" + branches + ", leafGenerator="
+ leafGenerator + "]";
}
}
public sealed class Tree<L extends Leaf> permits DeciduousTree, ConiferTree
{
private int id;
private String name;
private int age;
private Trunk<L> trunk;
public Tree(int id, String name, int age, Trunk<L> trunk, Supplier<L> leafGenerator) {
this.id = id;
this.name = name;
this.age = age;
this.trunk = trunk.with(leafGenerator);
}
public static <L extends Leaf> Tree<L> generate(int id, String name, Supplier<L> leafGenerator) {
return new Tree<L>(id, name, 0, new Trunk<L>(3, 1, new HashSet<>(), leafGenerator), leafGenerator);
}
public void grow() {
this.age++;
this.trunk.grow();
}
@Override
public String toString() {
return "\n\tTree [id=" + id + ", name=" + name + ", age=" + age + ", trunk=" + trunk + "]";
}
}
public final class DeciduousTree<L extends FlatLeaf> extends Tree<L> {
public DeciduousTree(int id, String name, int age, Trunk<L> trunk, Supplier<L> leafGenerator) {
super(id, name, age, trunk, leafGenerator);
}
}
public final class ConiferTree<L extends NeedleLeaf> extends Tree<L> {
public ConiferTree(int id, String name, int age, Trunk<L> trunk, Supplier<L> leafGenerator) {
super(id, name, age, trunk, leafGenerator);
}
}
public static void main(String[] args)
{
Tree<FlatLeaf> tree = Tree.generate(1, "Deciduous", (() -> new FlatLeaf(1)));
System.out.println(tree);
System.out.println(new ConiferTree<>(2, "Conny", 0, new Trunk<NeedleLeaf>(0, 0, null, null), () -> new NeedleLeaf(1)));
System.out.println("\n\n\n");
for (int i = 0; i < 3; i++) {
tree.grow();
System.out.println(tree);
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | davidalayachew |
