Use Java polymorphism to new corresponding object (StringBuffer or StringBuilder)

huangapple go评论66阅读模式
英文:

Use Java polymorphism to new corresponding object (StringBuffer or StringBuilder)

问题

I understand your question. You can use polymorphism in Java to fix your code by creating an interface or an abstract class that both StringBuffer and StringBuilder can implement or extend. Here's an example using an interface:

public interface MessageBuilder {
    String buildMsg(String name, int age);
}

Then, you can implement this interface in both StringBuffer and StringBuilder classes:

public class StringBufferBuilder implements MessageBuilder {
    @Override
    public String buildMsg(String name, int age) {
        StringBuffer sb = new StringBuffer();
        return sb.append("name: ").append(name).append(", age: ").append(age).toString();
    }
}

public class StringBuilderBuilder implements MessageBuilder {
    @Override
    public String buildMsg(String name, int age) {
        StringBuilder sb = new StringBuilder();
        return sb.append("name: ").append(name).append(", age: ").append(age).toString();
    }
}

Now, in your Builder class, you can use polymorphism to create an instance of the appropriate builder based on the threadSafe flag:

public class Builder {
    private boolean threadSafe;

    public Builder(boolean threadSafe) {
        this.threadSafe = threadSafe;
    }

    public String buildMsg(String name, int age) {
        MessageBuilder messageBuilder = threadSafe ? new StringBufferBuilder() : new StringBuilderBuilder();
        return messageBuilder.buildMsg(name, age);
    }
}

With this approach, you eliminate redundancy and use polymorphism to create the correct MessageBuilder implementation based on the threadSafe flag.

英文:

Actually, I want to use JAVA polymorphism to design a function that it can return a StringBuffer or StringBuilder for me depends on different scenario. My error code is

public class Builder{
    private boolean threadSafe;
    public Builder(boolean threadSafe) {
        this.threadSafe = threadSafe;
}
    public String buildMsg(String name, int age) {
       AbstractStringBuilder asb;
       asb = threadSafe ? new StringBuffer() : new StringBuilder();
       return asb.append("name: ").append(name).append(", age: ").append(age).toString();
}
}

The code can not be compiled. The error message is 'java.lang.AbstractStringBuilder' is not public in 'java.lang'. Cannot be accessed from outside package.
So I try another method, use Appendable instead of AbstractStringBuilder, it still doesn't work.
Until, I write code like this:

public class Builder{
    private boolean threadSafe;
    public Builder(boolean threadSafe) {
        this.threadSafe = threadSafe;
}
    public String buildMsg(String name, int age) {
       if (threadSafe) {
        StringBuffer sb = new StringBuffer();
        return sb.append("name: ").append(name).append(", age: ").append(age).toString();
    } else {
        StringBuilder sb = new StringBuilder();
        return sb.append("name: ").append(name).append(", age: ").append(age).toString();
    }
}
}

The code is obviously redundant. So how can I use polymorphism to fix my code.
(My English is not very well, hope you understand well)

答案1

得分: 0

StringBuffer - 虽然是线程安全的 - 只有在允许不同线程混合操作时才有保证:

Thread1:

sb.append("a").append("b").append("\n");

Thread2:

sb.append("c").append("d").append("\n");

可能会导致:

"acb\nd\n"

因此,使用StringBuffer时,可能需要:

sb.append("a" + "b" + "\n");

这在明显的理由下是荒谬的。违反时也容易出错,因为错误的sb.append(...).append(...)可能不会被检测到。

因此,最好使用线程安全的日志记录器或自己的同步方法(可能围绕StringWriter)。此外,由于这些类是final的,所以它们真的是一条死胡同。

英文:

StringBuffer - though thread safe - is only warranted if you would allow different threads to mix things up:

Thread1:

sb.append("a").append("b").append("\n");

Thread2:

sb.append("c").append("d").append("\n");

Might result in:

"acb\nd\n"

So with StringBuffer you might need:

sb.append("a" + "b" + "\n");

which is ridiculous for obvious reasons. Also error prone when violated,
as an erroneous sb.append(...).append(...) might go undetected.

So it is preferable to use a thread-safe logger or your own synchronisation (around a StringWriter maybe?).

Also as these classes are final, it is really a dead-end.

答案2

得分: 0

我建议你只需这样做:

public String buildMsg(String name, int age) {
    StringBuilder sb = new StringBuilder();
    sb = sb
        .append("name: ")
        .append(name)
        .append(", age: ")
        .append(age);
    return sb.toString();
}

因为在这段代码中,关于多线程的问题是不存在的。而且这正是StringBuilder类的用途。

如果多个线程都要访问同一个StringBuffer对象,那么你应该使用StringBuffer。但是,由于你在创建对象后失去了对它的引用,因此没有其他线程能够访问该对象。

然而,要回答标题中的问题。如果你有两种类型,它们都实现了Foo方法,但没有共同的接口,就像这样:

public class A {
    public Res Foo(Args args) { ... }
}

public class B {
    public Res Foo(Args args) { ... }
}

那么你将始终需要一些分辨它们两个版本的Foo方法的方式。但是你可以为这两种类型分别实现一个适配器或包装器类型,就像这样:

public abstract WrapperBase {
    public abstract Res Foo(Args args);

    public static WrapperBase Wrap(A a) {
        return new AWrapper(a);
    }
    public static WrapperBase Wrap(B b) {
        return new BWrapper(b);
    }
    
    public static WrapperBase Wrap(Object obj) {
        if (obj instanceof A) {
            return new AWrapper((A)obj);
        } else if (obj instanceof B) {
            return new AWrapper((B)obj);
        } else {
            throw new Exception("无法包装");
        }
    }
}

class AWrapper extends WrapperBase {
    private A a;
    public AWrapper(A a) { this.a = a; }

    @Override
    public Res Foo(Args args) { return a.Foo(args); }
}

class BWrapper extends WrapperBase {
    private B b;
    public BWrapper(B b) { this.b = b; }

    @Override
    public Res Foo(Args args) { return b.Foo(args); }
}

不过,这种方法的缺点是,如果你要包装大量的方法,这可能会变得很复杂。

英文:

I would to advice you to just do

public String buildMsg(String name, int age) {
    StringBuilder sb = new StringBuilder();
    sb = sb
        .append("name: ")
        .append(name)
        .append(", age: ")
        .append(age);
    return sb.toString();
}

since there are no problems regarding multi-threading in this code. Also this is exactly what the StringBuilder class is for.

You should use the StringBuffer if multiple threads have access to the same StringBuffer object, but since you create the object and at the end lose the reference to it, no other thread can access the object.


However, to answer the question in the title.
If you have two types that both implement the method Foo but without a common interface, like:

public class A {
    public Res Foo(Args args) { ... }
}

public class B {
    public Res Foo(Args args) { ... }
}

Then you will always need some casing to distinguish between both version of Foo.
But you can implement a Adapter or Wrapper type for both types, like:

public abstract WrapperBase {
    public abstract Res Foo(Args args);

    public static WrapperBase Wrap(A a) {
        return new AWrapper(a);
    }
    public static WrapperBase Wrap(B b) {
        return new BWrapper(b);
    }
    // or
    public static WrapperBase Wrap(Object obj) {
        if (obj instanceof A) {
            return new AWrapper((A)obj);
        } else if (obj instanceof B) {
            return new AWrapper((B)obj);
        } else {
            throw new Exception("Can not be wrapped");
        }
    }
}

class AWrapper extends WrapperBase {
    private A a;
    public AWrapper(A a) { this.a = a; }

    @override
    public Res Foo(Args args) { return a.Foo(args); }
}

class BWrapper extends WrapperBase {
    private B b;
    public BWrapper(B b) { this.b = b; }

    @override
    public Res Foo(Args args) { return b.Foo(args); }
}

The drawback of this is this can get out of hand if you have a larg amount of methods that you want to wrap up.

huangapple
  • 本文由 发表于 2020年8月6日 20:28:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/63283648.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定