3

Something like this:

MyFixedLengthStringBuilder sb = new MyFixedLengthStringBuilder(2);
sb.append("123"); // <=== throws ArrayIndexOfOutBoundsException!

The rest would be exactly the same. Basically I need a StringBuilder that can never grow.

I see that StringBuilder is final so it can not be extended. Delegation sounds like my only path here. Do you see any other hack/idea/solution for my need?

  • I don't think there is one with "exactly the same" functionalities (and methods). It seems like you're trying to limit the length, so you could just check the length before appending (and if it's longer, then throw an exception). – David Lee Jul 08 '21 at 14:20
  • @DavidLee Hi David. This class will be used by customers. So I can't just tell them to do that. The class has to enforce/guarantee the fixed-length. – Richard Brason Jul 08 '21 at 14:22
  • You can just extend `StringBuilder` and override the `append()` method. – David Lee Jul 08 '21 at 14:23
  • 1
    @DavidLee Hi David. As I said in the question, `StringBuilder` is *final* and cannot be extended. – Richard Brason Jul 08 '21 at 14:28
  • 1
    what is the expected value stored in `sb` if one performs `sb.append('123')` followed by `sb.append('456')` – Lovesh Dongre Jul 08 '21 at 14:28
  • 1
    @LoveshDongre An ArrayIndexOutOfBoundsException will be thrown – Spectric Jul 08 '21 at 14:32
  • @RichardBrason My bad. Perhaps you can use `StringBuilder` in your class. – David Lee Jul 08 '21 at 14:34
  • Have a class that extends the same interfaces StringBuilder does, holds an instance of it as one of its private fields and implements the behaviours you don't need to change simply by delegating it to its StringBuilder instance – Aaron Jul 08 '21 at 14:36
  • @Aaron The requested `append` method is a method of `StringBuilder` itself. – Izruo Jul 08 '21 at 14:37
  • @LoveshDongre If the max size is 2 and you do sb.append("123"), the store value will be "12" but most importantly, an ArrayIndexOutOfBoundsException will be thrown. – Richard Brason Jul 08 '21 at 14:37
  • @Izruo You are correct. `append` is a method of StringBuilder itself. My intention is to make sure **delegation** is my only option before going that path. I was hoping there was a clever way of doing that, even if it was a hack. – Richard Brason Jul 08 '21 at 14:39
  • Just use `String`, it can never grow. – paladin Jul 08 '21 at 15:58
  • 1
    @paladin *"Just use String, it can never grow"* `String` is immutable which means it can't change. So any operation to it must return a new String. So in essence it can grow to an indefinite size. – WJS Jul 08 '21 at 17:13
  • @WJS lol, your logic... I know that `String` is immutable and you know that `String` is immutable but you also say it can grow to an indefinite size. Please tell me, in what kind of logic is a thing able to grow when it's also immutable. Creating a new object is something completely different than to change an object. – paladin Jul 08 '21 at 18:58
  • 1
    @paladin you completely missed the point. The OP wants to modify it up to a limit. Since it is immutable, you can't modify it at all so you have to create a new string. So there is no way to limit how many times you create a new String unless you constantly check the length before doing so. Your suggestion does not make any sense. – WJS Jul 08 '21 at 19:54
  • @WJS so OP want's to have some kind of C string, okay, how about `char[] cLikeString = new char[20];`? – paladin Jul 08 '21 at 20:11
  • That would limit the contents and be able to be modified. And it could be converted to a String. But the problem is that the OP wants the functionality of a `StringBuilder` with a fixed size. So the OP would actually have to write their own implementation with the required methods and then to check for an index out of bounds exception when attempts are made to add more characters than the array can handle. Ideally, subclassing StringBuilder would be the ultimate solution. But it is declared final so subclassing can't be done. – WJS Jul 08 '21 at 20:16
  • @WJS just use `myStringBuffer.setLength(20);` after every string _"growing"_ command. – paladin Jul 08 '21 at 20:20

3 Answers3

2

You could so something like this. You would still have to have some overloaded methods to handle different argument counts and types but it wouldn't have to be a full delegation. This is just a simple example. It could be worked into a class. EDIT: Modified to handle most methods.

interface TriConsumer<T,R,S> {
      public void accept(T t, R r, S s);
}
public class StringBuilderSol {
    static int size = 20;
    static StringBuilder sb = new StringBuilder(size);
    
    
    public static void main(String[] args) {

        execute(sb::append, "This is fun!");
        System.out.println(sb);
        execute(sb::append, "Java!");
        System.out.println(sb);
        execute(sb::delete, 0,3);
        execute(sb::replace,0,1,"I");
        execute(sb::insert, 1, " like ");
        execute(sb::delete, 7,15);
        System.out.println(sb);
        execute(sb::append, "time to crash");
        
    }
    
    public static <T> void execute(Consumer<T> con,
            T v) {
        con.accept(v);
        checkLength();
    }
    
    public static <T,R> void execute(
            BiConsumer<T, R> biCon,
            T index, R val) {
        biCon.accept(index, val);
        checkLength();
    }

    public static <T,R,S> void execute(
            TriConsumer<T, R, S> triCon,
            T index, R arg1, S arg2) {
        triCon.accept(index, arg1, arg2);
        checkLength();
    }
    
    public static void checkLength() {
        if (sb.length() > size) {
            throw new IndexOutOfBoundsException();
        }
    }

}

Prints

This is fun!
This is fun!Java!
I like Java!
Exception in thread "main" java.lang.IndexOutOfBoundsException
    at stackOverflow.StringBuilderSol.checkLength(StringBuilderSol.java:50)
    at stackOverflow.StringBuilderSol.execute(StringBuilderSol.java:32)
    at stackOverflow.StringBuilderSol.main(StringBuilderSol.java:25)


Another option (but one that has its own problems) is to set up a timer to periodically check the size of the StringBuilder.

WJS
  • 36,363
  • 4
  • 24
  • 39
  • 1
    You can also declare `Consumer fnc`, since you don't actually use the return value. – Izruo Jul 08 '21 at 14:55
  • I modified this to use generic types for flexibility. And as @izruo said, you could use consumers in place of functions. For the three option version, an functional interface would need to be created. – WJS Jul 08 '21 at 15:11
1

If I could create my own class, it would go something like this and based on requirements more things could be added

public class MyFixedLengthStringBuilder {

    StringBuilder sb;
    int size;
    
    public MyFixedLengthStringBuilder(int size) {
        if(size < 0)
            throw new IllegalArgumentException();
        this.size = size;
        sb = new StringBuilder(size);
    }
    
    public void append(String str) {
        sb.append(str);
        if(sb.length() > size) {
            sb.setLength(size);
            throw new ArrayIndexOutOfBoundsException();
        }
    }
    
}
Lovesh Dongre
  • 1,294
  • 8
  • 23
0

First of all, the customer requirement is insufficiently specified: Are they requesting a class that is assignment-compatible with StringBuilder or are they simply requesting a class with a specific functionality?

The former is technically impossible, since StringBuilder is declared as final, thus any variable of type StringBuilder will always have the same behavior.

The latter allows you to specify your own new class with your own methods. As for the requirement, the customer likely specified "I just need everything from StringBuilder.", because they didn't bother to actually check what they need precisely. I think it is quite likely that you will be able to craft a class with just a few methods fit for the customer's needs, if you both invest some work into narrowing down the requirement.

And yes, as you already mentioned, in this case it is a good idea to use a composition with the original StringBuilder class.


As you were asking for a hack, you could theoretically write your own implementation of java.lang.StringBuilder and provide a custom ClassLoader that loads your implementation instead of the standard java one. I hope it is obvious that this is not a good practice, though.

Izruo
  • 2,246
  • 1
  • 11
  • 23