【问题标题】:Java builder classes generics hierarchy: how to make build() return correct methodJava builder 类泛型层次结构:如何使 build() 返回正确的方法
【发布时间】:2019-11-15 19:34:05
【问题描述】:

我有这个类结构:

                Stuff
              /       \
   Food
     |   
   FastFood                  Drink
     |                        |
   Pizza                    Juice
  /     \                   /    \
 |     SicilianPizza       |    OrangeJuice
 |                      AppleJuice
CaliforniaPizza

我可以使用这样的构建器创建披萨和果汁对象:

CaliforniaPizza californiaPizze = CaliforniaPizza.builder(2)
                .addTopping("Tomatoes").addOlives(true).build();
        SicilianPizza sicilianPizza = SicilianPizza.builder(1)
                .addTopping("Bacon").addCheese(false).build();

        AppleJuice appleJuice = AppleJuice.builder(40)
                .setPrice(120).setAlcoholVolume(0).setAppleColor("yellow").build();
        OrangeJuice orangeJuice = OrangeJuice.builder(35).setOrangeSize(8).build();

builder 的每个 setter 方法都返回正确的对象:

我想创建一个可以接受FastFood.Builder 对象的方法,并使用它来创建一个FastFood 对象,然后将其传递给另一个方法:

public void makeFat(FastFood fastFood)  {...}

但我不能:setter 方法不返回 FastFood,而是返回其父项:

我的泛型有问题吗?怎么可能实现?

package pizza;

import java.util.*;

class TestStuff {
    public static void main(String[] args) {
        CaliforniaPizza californiaPizze = CaliforniaPizza.builder(2)
                .addTopping("Tomatoes").addOlives(true).build();
        SicilianPizza sicilianPizza = SicilianPizza.builder(1)
                .addTopping("Bacon").addCheese(false).build();

        AppleJuice appleJuice = AppleJuice.builder(40)
                .setPrice(120).setAlcoholVolume(0).setAppleColor("yellow").build();
        OrangeJuice orangeJuice = OrangeJuice.builder(35).setOrangeSize(8).build();
    }

    public FastFood testFood(FastFood.Builder fastFoodBuilder)  {
       //return  fastFoodBuilder.setMealType("fd").addOlives(true).setPrice(20).build();
    }

    public void makeFat(FastFood fastFood)  {}
}



abstract class Stuff {
    protected double price;

    protected Stuff()  {}

    protected abstract class Builder<T extends Builder<T>>  {
        protected abstract Stuff build();
        protected abstract T self();

        public T setPrice(double price)  {
            Stuff.this.price = price;
            return self();
        }
    }
}

abstract class Food extends Stuff {
    protected String mealType; //breakfast/dinner/etc

    protected Food()  {}

    public abstract class Builder<T extends Builder<T>> extends Stuff.Builder<T> {
        protected abstract Food build();
        protected abstract T self();

        public T setMealType(String mealType)  {
            Food.this.mealType = mealType;
            return self();
        }
    }
}

abstract class FastFood extends Food  {
    protected int harm;

    protected FastFood() {}

    public abstract class Builder<T extends Builder<T>> extends Food.Builder<T> {
        protected abstract FastFood build();
        protected abstract T self();

        public T setHarm(int harm)  {
            FastFood.this.harm = harm;
            return self();
        }
    }
}

abstract class Pizza extends FastFood {
    protected List<String> toppings = new ArrayList<>();  //optional
    protected int size;  //obligatory

    protected Pizza(int size)  {this.size = size;}

    public abstract class Builder<T extends Builder<T>> extends FastFood.Builder<T>  {
        public T addTopping(String topping)  {
            toppings.add(topping);
            return self();
        }
    }
}

class CaliforniaPizza extends Pizza {
    private boolean addOlives;

    private CaliforniaPizza(int size) {super(size);}

    public static Builder builder(int size)  {return new CaliforniaPizza(size).new Builder();}

    public class Builder extends Pizza.Builder<Builder>  {
        @Override
        public CaliforniaPizza build()  {
            return CaliforniaPizza.this;
        }

        @Override
        public Builder self()  {return this;}

        public Builder addOlives(boolean addOlives)  {
            CaliforniaPizza.this.addOlives = addOlives;
            return this;
        }
    }
}

class SicilianPizza extends Pizza {
    private boolean addCheese;

    private SicilianPizza(int size)  {super(size);}

    public static Builder builder(int size)  {
        return new SicilianPizza(size).new Builder();
    }

    public class Builder extends Pizza.Builder<Builder>  {
        @Override
        public SicilianPizza build()  {return SicilianPizza.this;}

        @Override
        public Builder self()  {return this;}

        public Builder addCheese(boolean addCheese)  {
            SicilianPizza.this.addCheese = addCheese;
            return this;
        }
    }
}

abstract class Drink extends Stuff {
    protected double density;
    protected double alcoholVolume;

    protected Drink(double density)  {this.density = density;}

    public abstract class Builder<T extends Builder<T>> extends Stuff.Builder<T>  {
        protected abstract Drink build();
        protected abstract T self();

        public T setAlcoholVolume(double alcoholVolume)  {
            Drink.this.alcoholVolume = alcoholVolume;
            return self();
        }
    }
}

abstract class Juice extends Drink {
    private String color;

    protected Juice(double density)  {super(density);}

    public abstract class Builder<T extends Builder<T>> extends Drink.Builder<T>  {
        public Builder<T> setColor(String color)  {
            Juice.this.color = color;
            return self();
        }
    }
}
class AppleJuice extends Juice {
    private String appleColor;

    private AppleJuice(double density)  {super(density);}

    public static Builder builder(double density)  {return new AppleJuice(density).new Builder();}

    public class Builder extends Juice.Builder<Builder>  {
        @Override
        public AppleJuice build()  {
            return AppleJuice.this;
        }

        @Override
        public Builder self()  {
            return this;
        }

        public Builder setAppleColor(String appleColor)  {
            AppleJuice.this.appleColor = appleColor;
            return this;
        }
    }
}

class OrangeJuice extends Juice{
    private int orangeSize;

    private OrangeJuice(double density)  {super(density);}

    public static Builder builder(double density)  {return new OrangeJuice(density).new Builder();}

    public class Builder extends Juice.Builder<Builder>  {
        @Override
        public OrangeJuice build()  {return OrangeJuice.this;}

        @Override
        public Builder self()  {return this;}

        public Builder setOrangeSize(int orangeSize)  {
            OrangeJuice.this.orangeSize = orangeSize;
            return this;
        }
    }
}

【问题讨论】:

  • 从表中检索到的前 10 个条目对于“amenities”具有 NULL 或空字符串并不表示错误。您不能在消火栓处点三明治,这不是错误。试试SELECT amenity FROM planet_osm_point where amenity is not null and amenity &lt;&gt; ''LIMIT 10
  • 这似乎是你的IDE的问题。 Eclipse 将 FastFood2 推断为 build()。然而,如果您将方法签名转换为 FastFood testBuilder(FastFood2.Builder&lt;? extends FastFood2.Builder&lt;?&gt;&gt; builder),您的 IDE 可能会恢复。
  • @CoronA 抱歉,我意识到我的最后一个代码更难使用并对其进行了编辑。现在可以一次全部复制粘贴。我现在就试试你的建议!

标签: java generics design-patterns


【解决方案1】:

你有这行:

public FastFood testFood(FastFood.Builder fastFoodBuilder) {

还有:

abstract class FastFood extends Food  {
    ...
    public abstract class Builder<T extends Builder<T>> extends Food.Builder<T> {

应该有一个警告说FastFood.Builder是一个泛型类型。

参数类型需要适当地通用。可以使用通配符(将推断出约束)。

public FastFood testFood(FastFood.Builder<?> fastFoodBuilder) {

【讨论】:

    【解决方案2】:

    代码有一些问题。

    FastFood.Builder 类是通用类:class Builder&lt;T extends Builder&lt;T&gt;&gt; extends Food.Builder&lt;T&gt;

    FastFood testFood(FastFood.Builder fastFoodBuilder) 方法中声明了构建器没有指定类型 T

    例如AppleJuice指定参数T类型:class Builder extends Juice.Builder&lt;Builder&gt;

    Drink.Builder 也会出现同样的问题:

    public static Drink testDrink(Drink.Builder drinkBuilder) {
        return drinkBuilder
                .setPrice(100) // returns Stuff.Builder
                .setAlcoholVolume(0.7) //Error: cannot resolve method
                .build();
    }
    

    为了解决这个问题,使方法testFood泛型接受参数FastFood.Builder&lt;T&gt;

    public static <T extends FastFood.Builder<T>> FastFood testFood(FastFood.Builder<T> fastFoodBuilder) {
        return fastFoodBuilder.setMealType("fd").setHarm(1).setPrice(20).build();
    }
    

    接受Drink.Builder的方法也是如此:

    public static <T extends Drink.Builder<T>> Drink testDrink(Drink.Builder<T> drinkBuilder) {
        return drinkBuilder.setPrice(100).setAlcoholVolume(0.7).build();
    }
    

    使用示例:

    FastFood californiaPizza1 = testFood(CaliforniaPizza.builder(2));
    

    为了调用addOlives(boolean) 方法并避免从FastFood 转换为具体类(例如CaliforniaPizza),从方法返回builder 更方便:

    public static <T extends FastFood.Builder<T>> T testFoodBuilder(FastFood.Builder<T> fastFoodBuilder) {
        return fastFoodBuilder.setMealType("fd").setHarm(1).setPrice(20);
    }
    

    及用法:

    CaliforniaPizza californiaPizza2 = 
                testFoodBuilder(CaliforniaPizza.builder(2))
                        .addOlives(true)
                        .build();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-14
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 2016-09-14
      • 2013-03-20
      • 1970-01-01
      相关资源
      最近更新 更多