You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/10/17 12:09:05 UTC

[commons-lang] branch master updated (c0f527e50 -> 57ddce119)

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


    from c0f527e50 Remove paren noise
     new a21fd72f8 Javadoc
     new 6c3ffa44f Javadoc
     new 57ddce119 Add NumberRange, DoubleRange, IntegerRange, LongRange.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/changes/changes.xml                            |   3 +-
 .../java/org/apache/commons/lang3/DoubleRange.java |  85 ++++
 .../org/apache/commons/lang3/IntegerRange.java     |  85 ++++
 .../java/org/apache/commons/lang3/LongRange.java   |  85 ++++
 .../UncheckedException.java => NumberRange.java}   |  25 +-
 src/main/java/org/apache/commons/lang3/Range.java  |  87 ++++-
 .../apache/commons/lang3/time/DurationUtils.java   |   5 +-
 .../org/apache/commons/lang3/DoubleRangeTest.java  | 412 ++++++++++++++++++++
 .../org/apache/commons/lang3/IntegerRangeTest.java | 411 ++++++++++++++++++++
 .../org/apache/commons/lang3/LongRangeTest.java    | 426 +++++++++++++++++++++
 .../java/org/apache/commons/lang3/RangeTest.java   |  60 ++-
 11 files changed, 1636 insertions(+), 48 deletions(-)
 create mode 100644 src/main/java/org/apache/commons/lang3/DoubleRange.java
 create mode 100644 src/main/java/org/apache/commons/lang3/IntegerRange.java
 create mode 100644 src/main/java/org/apache/commons/lang3/LongRange.java
 copy src/main/java/org/apache/commons/lang3/{exception/UncheckedException.java => NumberRange.java} (55%)
 create mode 100644 src/test/java/org/apache/commons/lang3/DoubleRangeTest.java
 create mode 100644 src/test/java/org/apache/commons/lang3/IntegerRangeTest.java
 create mode 100644 src/test/java/org/apache/commons/lang3/LongRangeTest.java


[commons-lang] 01/03: Javadoc

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git

commit a21fd72f85aa17524e5d130653b65d3e8ae9cdd1
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Oct 17 05:45:41 2022 -0400

    Javadoc
---
 src/test/java/org/apache/commons/lang3/RangeTest.java | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/test/java/org/apache/commons/lang3/RangeTest.java b/src/test/java/org/apache/commons/lang3/RangeTest.java
index 628192a5a..4ed18cdf4 100644
--- a/src/test/java/org/apache/commons/lang3/RangeTest.java
+++ b/src/test/java/org/apache/commons/lang3/RangeTest.java
@@ -31,9 +31,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 /**
- * <p>
- * Tests the methods in the {@link org.apache.commons.lang3.Range} class.
- * </p>
+ * Tests {@link Range}.
  */
 @SuppressWarnings("boxing")
 public class RangeTest extends AbstractLangTest {


[commons-lang] 02/03: Javadoc

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git

commit 6c3ffa44ffa31c256bbc73dd28d858c309fffda5
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Oct 17 05:54:19 2022 -0400

    Javadoc
---
 src/main/java/org/apache/commons/lang3/Range.java | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/Range.java b/src/main/java/org/apache/commons/lang3/Range.java
index c252a6c94..f34277828 100644
--- a/src/main/java/org/apache/commons/lang3/Range.java
+++ b/src/main/java/org/apache/commons/lang3/Range.java
@@ -57,7 +57,7 @@ public final class Range<T> implements Serializable {
     private static final long serialVersionUID = 1L;
 
     /**
-     * Obtains a range with the specified minimum and maximum values (both inclusive).
+     * Creates a range with the specified minimum and maximum values (both inclusive).
      *
      * <p>The range uses the natural ordering of the elements to determine where
      * values lie in the range.</p>
@@ -77,7 +77,7 @@ public final class Range<T> implements Serializable {
     }
 
     /**
-     * Obtains a range with the specified minimum and maximum values (both inclusive).
+     * Creates a range with the specified minimum and maximum values (both inclusive).
      *
      * <p>The range uses the specified {@link Comparator} to determine where
      * values lie in the range.</p>
@@ -98,7 +98,7 @@ public final class Range<T> implements Serializable {
     }
 
     /**
-     * Obtains a range using the specified element as both the minimum
+     * Creates a range using the specified element as both the minimum
      * and maximum in this range.
      *
      * <p>The range uses the natural ordering of the elements to determine where
@@ -115,7 +115,7 @@ public final class Range<T> implements Serializable {
     }
 
     /**
-     * Obtains a range using the specified element as both the minimum
+     * Creates a range using the specified element as both the minimum
      * and maximum in this range.
      *
      * <p>The range uses the specified {@link Comparator} to determine where
@@ -165,7 +165,7 @@ public final class Range<T> implements Serializable {
      * @param comp  the comparator to be used, null for natural ordering
      */
     @SuppressWarnings("unchecked")
-    private Range(final T element1, final T element2, final Comparator<T> comp) {
+    Range(final T element1, final T element2, final Comparator<T> comp) {
         if (element1 == null || element2 == null) {
             throw new IllegalArgumentException("Elements in a range must not be null: element1=" +
                                                element1 + ", element2=" + element2);


[commons-lang] 03/03: Add NumberRange, DoubleRange, IntegerRange, LongRange.

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git

commit 57ddce119516f451168f4489f55f399fd9309b53
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Oct 17 08:08:59 2022 -0400

    Add NumberRange, DoubleRange, IntegerRange, LongRange.
    
    Fix changes.xml
---
 src/changes/changes.xml                            |   3 +-
 .../java/org/apache/commons/lang3/DoubleRange.java |  85 ++++
 .../org/apache/commons/lang3/IntegerRange.java     |  85 ++++
 .../java/org/apache/commons/lang3/LongRange.java   |  85 ++++
 .../java/org/apache/commons/lang3/NumberRange.java |  48 +++
 src/main/java/org/apache/commons/lang3/Range.java  |  77 +++-
 .../apache/commons/lang3/time/DurationUtils.java   |   5 +-
 .../org/apache/commons/lang3/DoubleRangeTest.java  | 412 ++++++++++++++++++++
 .../org/apache/commons/lang3/IntegerRangeTest.java | 411 ++++++++++++++++++++
 .../org/apache/commons/lang3/LongRangeTest.java    | 426 +++++++++++++++++++++
 .../java/org/apache/commons/lang3/RangeTest.java   |  56 ++-
 11 files changed, 1662 insertions(+), 31 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 21c646fd9..876ee71c3 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -103,7 +103,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action issue="LANG-1638" type="fix" dev="ggregory" due-to="Shailendra Soni, Michael Osipov, Arun Avanathan, Andrew Thomas, Bruno P. Kinoshita, Gary Gregory">Added docs regarding week year support #924.</action>
     <action issue="LANG-1691" type="fix" dev="ggregory" due-to="Thiyagarajan, Gary Gregory">ClassUtils.getShortCanonicalName doesn't use the canonicalName #949.</action>
     <action                   type="fix" dev="aherbert" due-to="Piotr Stawirej">Validate: Get error messages without using String.format when varargs is empty.</action>
-    <action                   type="fix" dev="ggregory" due-to="Arturo Bernal">Simplify expression (length is never < 0) #962.</action>
+    <action                   type="fix" dev="ggregory" due-to="Arturo Bernal">Simplify expression (length is never &lt; 0) #962.</action>
     <!-- ADD -->
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add GitHub coverage.yml.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add EnumUtils.getEnumSystemProperty(...).</action>
@@ -182,6 +182,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add BooleanUtils.values() and forEach().</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add ClassPathUtils.packageToPath(String) and pathToPackage(String)</action>
     <action                   type="add" dev="ggregory" due-to="Arturo Bernal">Add CalendarUtils#getDayOfYear() #968</action>
+    <action                   type="add" dev="ggregory" due-to="ary Gregory">Add NumberRange, DoubleRange, IntegerRange, LongRange.</action>
     <!-- UPDATE -->
     <action                   type="update" dev="ggregory" due-to="Dependabot, XenoAmess, Gary Gregory">Bump actions/cache from 2.1.4 to 3.0.10 #742, #752, #764, #833, #867, #959, #964.</action>
     <action                   type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump actions/checkout from 2 to 3.1.0 #819, #825, #859, #963.</action>
diff --git a/src/main/java/org/apache/commons/lang3/DoubleRange.java b/src/main/java/org/apache/commons/lang3/DoubleRange.java
new file mode 100644
index 000000000..0c6d5a971
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/DoubleRange.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3;
+
+/**
+ * Specializes {@link NumberRange} for {@link Double}s.
+ *
+ * <p>
+ * This class is not designed to interoperate with other NumberRanges
+ * </p>
+ *
+ * @since 3.13.0
+ */
+public final class DoubleRange extends NumberRange<Double> {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>
+     * The range uses the natural ordering of the elements to determine where values lie in the range.
+     * </p>
+     *
+     * <p>
+     * The arguments may be passed in the order (min,max) or (max,min). The getMinimum and getMaximum methods will return the correct values.
+     * </p>
+     *
+     * @param fromInclusive the first value that defines the edge of the range, inclusive.
+     * @param toInclusive the second value that defines the edge of the range, inclusive.
+     * @return the range object, not null.
+     */
+    public static DoubleRange of(final double fromInclusive, final double toInclusive) {
+        return of(Double.valueOf(fromInclusive), Double.valueOf(toInclusive));
+    }
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>
+     * The range uses the natural ordering of the elements to determine where values lie in the range.
+     * </p>
+     *
+     * <p>
+     * The arguments may be passed in the order (min,max) or (max,min). The getMinimum and getMaximum methods will return the correct values.
+     * </p>
+     *
+     * @param fromInclusive the first value that defines the edge of the range, inclusive.
+     * @param toInclusive the second value that defines the edge of the range, inclusive.
+     * @return the range object, not null.
+     * @throws IllegalArgumentException if either element is null.
+     */
+    public static DoubleRange of(final Double fromInclusive, final Double toInclusive) {
+        return new DoubleRange(fromInclusive, toInclusive);
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param number1 the first element, not null
+     * @param number2 the second element, not null
+     * @param comp the comparator to be used, null for natural ordering
+     * @throws NullPointerException when element1 is null.
+     * @throws NullPointerException when element2 is null.
+     */
+    private DoubleRange(final Double number1, final Double number2) {
+        super(number1, number2, null);
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/lang3/IntegerRange.java b/src/main/java/org/apache/commons/lang3/IntegerRange.java
new file mode 100644
index 000000000..3bc320cb9
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/IntegerRange.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3;
+
+/**
+ * Specializes {@link NumberRange} for {@link Integer}s.
+ *
+ * <p>
+ * This class is not designed to interoperate with other NumberRanges
+ * </p>
+ *
+ * @since 3.13.0
+ */
+public final class IntegerRange extends NumberRange<Integer> {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>
+     * The range uses the natural ordering of the elements to determine where values lie in the range.
+     * </p>
+     *
+     * <p>
+     * The arguments may be passed in the order (min,max) or (max,min). The getMinimum and getMaximum methods will return the correct values.
+     * </p>
+     *
+     * @param fromInclusive the first value that defines the edge of the range, inclusive.
+     * @param toInclusive the second value that defines the edge of the range, inclusive.
+     * @return the range object, not null.
+     */
+    public static IntegerRange of(final int fromInclusive, final int toInclusive) {
+        return of(Integer.valueOf(fromInclusive), Integer.valueOf(toInclusive));
+    }
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>
+     * The range uses the natural ordering of the elements to determine where values lie in the range.
+     * </p>
+     *
+     * <p>
+     * The arguments may be passed in the order (min,max) or (max,min). The getMinimum and getMaximum methods will return the correct values.
+     * </p>
+     *
+     * @param fromInclusive the first value that defines the edge of the range, inclusive.
+     * @param toInclusive the second value that defines the edge of the range, inclusive.
+     * @return the range object, not null.
+     * @throws IllegalArgumentException if either element is null.
+     */
+    public static IntegerRange of(final Integer fromInclusive, final Integer toInclusive) {
+        return new IntegerRange(fromInclusive, toInclusive);
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param number1 the first element, not null
+     * @param number2 the second element, not null
+     * @param comp the comparator to be used, null for natural ordering
+     * @throws NullPointerException when element1 is null.
+     * @throws NullPointerException when element2 is null.
+     */
+    private IntegerRange(final Integer number1, final Integer number2) {
+        super(number1, number2, null);
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/lang3/LongRange.java b/src/main/java/org/apache/commons/lang3/LongRange.java
new file mode 100644
index 000000000..55efe88ab
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/LongRange.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3;
+
+/**
+ * Specializes {@link NumberRange} for {@link Long}s.
+ *
+ * <p>
+ * This class is not designed to interoperate with other NumberRanges
+ * </p>
+ *
+ * @since 3.13.0
+ */
+public final class LongRange extends NumberRange<Long> {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>
+     * The range uses the natural ordering of the elements to determine where values lie in the range.
+     * </p>
+     *
+     * <p>
+     * The arguments may be passed in the order (min,max) or (max,min). The getMinimum and getMaximum methods will return the correct values.
+     * </p>
+     *
+     * @param fromInclusive the first value that defines the edge of the range, inclusive.
+     * @param toInclusive the second value that defines the edge of the range, inclusive.
+     * @return the range object, not null.
+     */
+    public static LongRange of(final long fromInclusive, final long toInclusive) {
+        return of(Long.valueOf(fromInclusive), Long.valueOf(toInclusive));
+    }
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>
+     * The range uses the natural ordering of the elements to determine where values lie in the range.
+     * </p>
+     *
+     * <p>
+     * The arguments may be passed in the order (min,max) or (max,min). The getMinimum and getMaximum methods will return the correct values.
+     * </p>
+     *
+     * @param fromInclusive the first value that defines the edge of the range, inclusive.
+     * @param toInclusive the second value that defines the edge of the range, inclusive.
+     * @return the range object, not null.
+     * @throws IllegalArgumentException if either element is null.
+     */
+    public static LongRange of(final Long fromInclusive, final Long toInclusive) {
+        return new LongRange(fromInclusive, toInclusive);
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param number1 the first element, not null
+     * @param number2 the second element, not null
+     * @param comp the comparator to be used, null for natural ordering
+     * @throws NullPointerException when element1 is null.
+     * @throws NullPointerException when element2 is null.
+     */
+    private LongRange(final Long number1, final Long number2) {
+        super(number1, number2, null);
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/lang3/NumberRange.java b/src/main/java/org/apache/commons/lang3/NumberRange.java
new file mode 100644
index 000000000..96afc3a5d
--- /dev/null
+++ b/src/main/java/org/apache/commons/lang3/NumberRange.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3;
+
+import java.util.Comparator;
+
+/**
+ * Specializes {@link Range} for {@link Number}s.
+ * <p>
+ * We only offer specializations for Integer, Long, and Double (like Java Streams).
+ * </p>
+ *
+ * @param <N> The Number class.
+ * @since 3.13.0
+ */
+public class NumberRange<N extends Number> extends Range<N> {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Creates an instance.
+     *
+     * @param number1 the first element, not null
+     * @param number2 the second element, not null
+     * @param comp the comparator to be used, null for natural ordering
+     * @throws NullPointerException when element1 is null.
+     * @throws NullPointerException when element2 is null.
+     */
+    public NumberRange(final N number1, final N number2, final Comparator<N> comp) {
+        super(number1, number2, comp);
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/lang3/Range.java b/src/main/java/org/apache/commons/lang3/Range.java
index f34277828..4318fb6bf 100644
--- a/src/main/java/org/apache/commons/lang3/Range.java
+++ b/src/main/java/org/apache/commons/lang3/Range.java
@@ -18,6 +18,7 @@ package org.apache.commons.lang3;
 
 import java.io.Serializable;
 import java.util.Comparator;
+import java.util.Objects;
 
 /**
  * An immutable range of objects from a minimum to maximum point inclusive.
@@ -30,7 +31,7 @@ import java.util.Comparator;
  * @param <T> The type of range values.
  * @since 3.0
  */
-public final class Range<T> implements Serializable {
+public class Range<T> implements Serializable {
 
     @SuppressWarnings({"rawtypes", "unchecked"})
     private enum ComparableComparator implements Comparator {
@@ -69,11 +70,14 @@ public final class Range<T> implements Serializable {
      * @param fromInclusive  the first value that defines the edge of the range, inclusive
      * @param toInclusive  the second value that defines the edge of the range, inclusive
      * @return the range object, not null
-     * @throws IllegalArgumentException if either element is null
+     * @throws NullPointerException when fromInclusive is null.
+     * @throws NullPointerException when toInclusive is null.
      * @throws ClassCastException if the elements are not {@link Comparable}
+     * @deprecated Use {@link #of(Comparable, Comparable)}.
      */
+    @Deprecated
     public static <T extends Comparable<? super T>> Range<T> between(final T fromInclusive, final T toInclusive) {
-        return between(fromInclusive, toInclusive, null);
+        return of(fromInclusive, toInclusive, null);
     }
 
     /**
@@ -90,9 +94,12 @@ public final class Range<T> implements Serializable {
      * @param toInclusive  the second value that defines the edge of the range, inclusive
      * @param comparator  the comparator to be used, null for natural ordering
      * @return the range object, not null
-     * @throws IllegalArgumentException if either element is null
+     * @throws NullPointerException when fromInclusive is null.
+     * @throws NullPointerException when toInclusive is null.
      * @throws ClassCastException if using natural ordering and the elements are not {@link Comparable}
+     * @deprecated Use {@link #of(Object, Object, Comparator)}.
      */
+    @Deprecated
     public static <T> Range<T> between(final T fromInclusive, final T toInclusive, final Comparator<T> comparator) {
         return new Range<>(fromInclusive, toInclusive, comparator);
     }
@@ -107,11 +114,11 @@ public final class Range<T> implements Serializable {
      * @param <T> the type of the elements in this range
      * @param element  the value to use for this range, not null
      * @return the range object, not null
-     * @throws IllegalArgumentException if the element is null
+     * @throws NullPointerException if the element is null
      * @throws ClassCastException if the element is not {@link Comparable}
      */
     public static <T extends Comparable<? super T>> Range<T> is(final T element) {
-        return between(element, element, null);
+        return of(element, element, null);
     }
 
     /**
@@ -125,11 +132,55 @@ public final class Range<T> implements Serializable {
      * @param element  the value to use for this range, must not be {@code null}
      * @param comparator  the comparator to be used, null for natural ordering
      * @return the range object, not null
-     * @throws IllegalArgumentException if the element is null
+     * @throws NullPointerException if the element is null
      * @throws ClassCastException if using natural ordering and the elements are not {@link Comparable}
      */
     public static <T> Range<T> is(final T element, final Comparator<T> comparator) {
-        return between(element, element, comparator);
+        return of(element, element, comparator);
+    }
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>The range uses the natural ordering of the elements to determine where
+     * values lie in the range.</p>
+     *
+     * <p>The arguments may be passed in the order (min,max) or (max,min).
+     * The getMinimum and getMaximum methods will return the correct values.</p>
+     *
+     * @param <T> the type of the elements in this range
+     * @param fromInclusive  the first value that defines the edge of the range, inclusive
+     * @param toInclusive  the second value that defines the edge of the range, inclusive
+     * @return the range object, not null
+     * @throws NullPointerException if either element is null
+     * @throws ClassCastException if the elements are not {@link Comparable}
+     * @since 3.13.0
+     */
+    public static <T extends Comparable<? super T>> Range<T> of(final T fromInclusive, final T toInclusive) {
+        return of(fromInclusive, toInclusive, null);
+    }
+
+    /**
+     * Creates a range with the specified minimum and maximum values (both inclusive).
+     *
+     * <p>The range uses the specified {@link Comparator} to determine where
+     * values lie in the range.</p>
+     *
+     * <p>The arguments may be passed in the order (min,max) or (max,min).
+     * The getMinimum and getMaximum methods will return the correct values.</p>
+     *
+     * @param <T> the type of the elements in this range
+     * @param fromInclusive  the first value that defines the edge of the range, inclusive
+     * @param toInclusive  the second value that defines the edge of the range, inclusive
+     * @param comparator  the comparator to be used, null for natural ordering
+     * @return the range object, not null
+     * @throws NullPointerException when fromInclusive is null.
+     * @throws NullPointerException when toInclusive is null.
+     * @throws ClassCastException if using natural ordering and the elements are not {@link Comparable}
+     * @since 3.13.0
+     */
+    public static <T> Range<T> of(final T fromInclusive, final T toInclusive, final Comparator<T> comparator) {
+        return new Range<>(fromInclusive, toInclusive, comparator);
     }
 
     /**
@@ -163,13 +214,13 @@ public final class Range<T> implements Serializable {
      * @param element1  the first element, not null
      * @param element2  the second element, not null
      * @param comp  the comparator to be used, null for natural ordering
+     * @throws NullPointerException when element1 is null.
+     * @throws NullPointerException when element2 is null.
      */
     @SuppressWarnings("unchecked")
     Range(final T element1, final T element2, final Comparator<T> comp) {
-        if (element1 == null || element2 == null) {
-            throw new IllegalArgumentException("Elements in a range must not be null: element1=" +
-                                               element1 + ", element2=" + element2);
-        }
+        Objects.requireNonNull(element1, "element1");
+        Objects.requireNonNull(element2, "element2");
         if (comp == null) {
             this.comparator = ComparableComparator.INSTANCE;
         } else {
@@ -357,7 +408,7 @@ public final class Range<T> implements Serializable {
         }
         final T min = getComparator().compare(minimum, other.minimum) < 0 ? other.minimum : minimum;
         final T max = getComparator().compare(maximum, other.maximum) < 0 ? maximum : other.maximum;
-        return between(min, max, getComparator());
+        return of(min, max, getComparator());
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/lang3/time/DurationUtils.java b/src/main/java/org/apache/commons/lang3/time/DurationUtils.java
index afb3804a5..2a6d7ebd7 100644
--- a/src/main/java/org/apache/commons/lang3/time/DurationUtils.java
+++ b/src/main/java/org/apache/commons/lang3/time/DurationUtils.java
@@ -24,8 +24,8 @@ import java.time.temporal.Temporal;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.commons.lang3.LongRange;
 import org.apache.commons.lang3.ObjectUtils;
-import org.apache.commons.lang3.Range;
 import org.apache.commons.lang3.function.FailableBiConsumer;
 import org.apache.commons.lang3.function.FailableConsumer;
 import org.apache.commons.lang3.function.FailableRunnable;
@@ -41,8 +41,7 @@ public class DurationUtils {
     /**
      * An Integer Range that accepts Longs.
      */
-    static final Range<Long> LONG_TO_INT_RANGE = Range.between(NumberUtils.LONG_INT_MIN_VALUE,
-            NumberUtils.LONG_INT_MAX_VALUE);
+    static final LongRange LONG_TO_INT_RANGE = LongRange.of(NumberUtils.LONG_INT_MIN_VALUE, NumberUtils.LONG_INT_MAX_VALUE);
 
     /**
      * Accepts the function with the duration as a long milliseconds and int nanoseconds.
diff --git a/src/test/java/org/apache/commons/lang3/DoubleRangeTest.java b/src/test/java/org/apache/commons/lang3/DoubleRangeTest.java
new file mode 100644
index 000000000..75b9ab66a
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/DoubleRangeTest.java
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Comparator;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link DoubleRange}.
+ */
+@SuppressWarnings("boxing")
+public class DoubleRangeTest extends AbstractLangTest {
+
+    private static DoubleRange of(final double min, final double max) {
+        return DoubleRange.of(min, max);
+    }
+
+    private static DoubleRange of(final Double min, final Double max) {
+        return DoubleRange.of(min, max);
+    }
+
+    private DoubleRange range1;
+
+    private DoubleRange range2;
+
+    private DoubleRange range3;
+
+    private DoubleRange rangeFull;
+
+    @BeforeEach
+    public void setUp() {
+        range1 = of(10, 20);
+        range2 = of(10, 20);
+        range3 = of(-2, -1);
+        rangeFull = of(Double.MIN_VALUE, Double.MAX_VALUE);
+    }
+
+    @Test
+    public void testContainsInt() {
+        assertFalse(range1.contains(null));
+
+        assertTrue(rangeFull.contains(Double.MIN_VALUE));
+        assertTrue(rangeFull.contains(Double.MAX_VALUE));
+
+        assertFalse(range1.contains(5d));
+        assertTrue(range1.contains(10d));
+        assertTrue(range1.contains(15d));
+        assertTrue(range1.contains(20d));
+        assertFalse(range1.contains(25d));
+    }
+
+    @Test
+    public void testContainsRange() {
+
+        // null handling
+        assertFalse(range1.containsRange(null));
+
+        // easy inside range
+        assertTrue(range1.containsRange(Range.of(12d, 18d)));
+        assertTrue(range1.containsRange(of(12, 18)));
+
+        // outside range on each side
+        assertFalse(range1.containsRange(Range.of(32d, 45d)));
+        assertFalse(range1.containsRange(of(32, 45)));
+        assertFalse(range1.containsRange(Range.of(2d, 8d)));
+        assertFalse(range1.containsRange(of(2, 8)));
+
+        // equals range
+        assertTrue(range1.containsRange(Range.of(10d, 20d)));
+        assertTrue(range1.containsRange(of(10, 20)));
+
+        // overlaps
+        assertFalse(range1.containsRange(Range.of(9d, 14d)));
+        assertFalse(range1.containsRange(of(9, 14)));
+        assertFalse(range1.containsRange(Range.of(16d, 21d)));
+        assertFalse(range1.containsRange(of(16, 21)));
+
+        // touches lower boundary
+        assertTrue(range1.containsRange(Range.of(10d, 19d)));
+        assertTrue(range1.containsRange(of(10, 19)));
+        assertFalse(range1.containsRange(Range.of(10d, 21d)));
+        assertFalse(range1.containsRange(of(10, 21)));
+
+        // touches upper boundary
+        assertTrue(range1.containsRange(Range.of(11d, 20d)));
+        assertTrue(range1.containsRange(of(11, 20)));
+        assertFalse(range1.containsRange(Range.of(9d, 20d)));
+        assertFalse(range1.containsRange(of(9, 20)));
+
+        // negative
+        assertFalse(range1.containsRange(Range.of(-11d, -18d)));
+        assertFalse(range1.containsRange(of(-11, -18)));
+    }
+
+    @Test
+    public void testElementCompareTo() {
+        assertThrows(NullPointerException.class, () -> range1.elementCompareTo(null));
+
+        assertEquals(-1, range1.elementCompareTo(5d));
+        assertEquals(0, range1.elementCompareTo(10d));
+        assertEquals(0, range1.elementCompareTo(15d));
+        assertEquals(0, range1.elementCompareTo(20d));
+        assertEquals(1, range1.elementCompareTo(25d));
+    }
+
+    @Test
+    public void testEqualsObject() {
+        assertEquals(range1, range1);
+        assertEquals(range1, range2);
+        assertEquals(range2, range2);
+        assertEquals(range1, range1);
+        assertEquals(range2, range2);
+        assertEquals(range3, range3);
+        assertNotEquals(range2, range3);
+        assertNotEquals(null, range2);
+        assertNotEquals("Ni!", range2);
+    }
+
+    @Test
+    public void testFit() {
+        assertEquals(range1.getMinimum(), range1.fit(Double.MIN_VALUE));
+        assertEquals(range1.getMinimum(), range1.fit(range1.getMinimum()));
+        assertEquals(range1.getMaximum(), range1.fit(Double.MAX_VALUE));
+        assertEquals(range1.getMaximum(), range1.fit(range1.getMaximum()));
+        assertEquals(15, range1.fit(15d));
+    }
+
+    @Test
+    public void testFitNull() {
+        assertThrows(NullPointerException.class, () -> {
+            range1.fit(null);
+        });
+    }
+
+    @Test
+    public void testGetMaximum() {
+        assertEquals(20d, range1.getMaximum());
+    }
+
+    @Test
+    public void testGetMinimum() {
+        assertEquals(10d, range1.getMinimum());
+    }
+
+    @Test
+    public void testHashCode() {
+        assertEquals(range1.hashCode(), range2.hashCode());
+        assertNotEquals(range1.hashCode(), range3.hashCode());
+
+        assertEquals(range1.hashCode(), range1.hashCode());
+        assertTrue(range1.hashCode() != 0);
+    }
+
+    @Test
+    public void testIntersectionWith() {
+        assertSame(range1, range1.intersectionWith(range1));
+
+        assertEquals(Range.of(10d, 15d), range1.intersectionWith(Range.of(5d, 15d)));
+    }
+
+    @Test
+    public void testIntersectionWithNonOverlapping() {
+        assertThrows(IllegalArgumentException.class, () -> range1.intersectionWith(Range.of(0d, 9d)));
+    }
+
+    @Test
+    public void testIntersectionWithNull() {
+        assertThrows(IllegalArgumentException.class, () -> range1.intersectionWith(null));
+    }
+
+    @Test
+    public void testIsAfter() {
+        assertFalse(range1.isAfter(null));
+
+        assertTrue(range1.isAfter(5d));
+        assertFalse(range1.isAfter(10d));
+        assertFalse(range1.isAfter(15d));
+        assertFalse(range1.isAfter(20d));
+        assertFalse(range1.isAfter(25d));
+    }
+
+    @Test
+    public void testIsAfterRange() {
+        assertFalse(range1.isAfterRange(null));
+
+        assertTrue(range1.isAfterRange(Range.of(5d, 9d)));
+
+        assertFalse(range1.isAfterRange(Range.of(5d, 10d)));
+        assertFalse(range1.isAfterRange(Range.of(5d, 20d)));
+        assertFalse(range1.isAfterRange(Range.of(5d, 25d)));
+        assertFalse(range1.isAfterRange(Range.of(15d, 25d)));
+
+        assertFalse(range1.isAfterRange(Range.of(21d, 25d)));
+
+        assertFalse(range1.isAfterRange(Range.of(10d, 20d)));
+    }
+
+    @Test
+    public void testIsBefore() {
+        assertFalse(range1.isBefore(null));
+
+        assertFalse(range1.isBefore(5d));
+        assertFalse(range1.isBefore(10d));
+        assertFalse(range1.isBefore(15d));
+        assertFalse(range1.isBefore(20d));
+        assertTrue(range1.isBefore(25d));
+    }
+
+    @Test
+    public void testIsBeforeIntegerRange() {
+        assertFalse(range1.isBeforeRange(null));
+
+        assertFalse(range1.isBeforeRange(of(5, 9)));
+
+        assertFalse(range1.isBeforeRange(of(5, 10)));
+        assertFalse(range1.isBeforeRange(of(5, 20)));
+        assertFalse(range1.isBeforeRange(of(5, 25)));
+        assertFalse(range1.isBeforeRange(of(15, 25)));
+
+        assertTrue(range1.isBeforeRange(of(21, 25)));
+
+        assertFalse(range1.isBeforeRange(of(10, 20)));
+    }
+
+    @Test
+    public void testIsBeforeRange() {
+        assertFalse(range1.isBeforeRange(null));
+
+        assertFalse(range1.isBeforeRange(Range.of(5d, 9d)));
+
+        assertFalse(range1.isBeforeRange(Range.of(5d, 10d)));
+        assertFalse(range1.isBeforeRange(Range.of(5d, 20d)));
+        assertFalse(range1.isBeforeRange(Range.of(5d, 25d)));
+        assertFalse(range1.isBeforeRange(Range.of(15d, 25d)));
+
+        assertTrue(range1.isBeforeRange(Range.of(21d, 25d)));
+
+        assertFalse(range1.isBeforeRange(Range.of(10d, 20d)));
+    }
+
+    @Test
+    public void testIsEndedBy() {
+        assertFalse(range1.isEndedBy(null));
+
+        assertFalse(range1.isEndedBy(5d));
+        assertFalse(range1.isEndedBy(10d));
+        assertFalse(range1.isEndedBy(15d));
+        assertTrue(range1.isEndedBy(20d));
+        assertFalse(range1.isEndedBy(25d));
+    }
+
+    @Test
+    public void testIsOverlappedByIntegerRange() {
+
+        // null handling
+        assertFalse(range1.isOverlappedBy(null));
+
+        // easy inside range
+        assertTrue(range1.isOverlappedBy(of(12, 18)));
+
+        // outside range on each side
+        assertFalse(range1.isOverlappedBy(of(32, 45)));
+        assertFalse(range1.isOverlappedBy(of(2, 8)));
+
+        // equals range
+        assertTrue(range1.isOverlappedBy(of(10, 20)));
+
+        // overlaps
+        assertTrue(range1.isOverlappedBy(of(9, 14)));
+        assertTrue(range1.isOverlappedBy(of(16, 21)));
+
+        // touches lower boundary
+        assertTrue(range1.isOverlappedBy(of(10, 19)));
+        assertTrue(range1.isOverlappedBy(of(10, 21)));
+
+        // touches upper boundary
+        assertTrue(range1.isOverlappedBy(of(11, 20)));
+        assertTrue(range1.isOverlappedBy(of(9, 20)));
+
+        // negative
+        assertFalse(range1.isOverlappedBy(of(-11, -18)));
+
+        // outside range whole range
+        assertTrue(range1.isOverlappedBy(of(9, 21)));
+    }
+
+    @Test
+    public void testIsOverlappedByRange() {
+
+        // null handling
+        assertFalse(range1.isOverlappedBy(null));
+
+        // easy inside range
+        assertTrue(range1.isOverlappedBy(Range.of(12d, 18d)));
+
+        // outside range on each side
+        assertFalse(range1.isOverlappedBy(Range.of(32d, 45d)));
+        assertFalse(range1.isOverlappedBy(Range.of(2d, 8d)));
+
+        // equals range
+        assertTrue(range1.isOverlappedBy(Range.of(10d, 20d)));
+
+        // overlaps
+        assertTrue(range1.isOverlappedBy(Range.of(9d, 14d)));
+        assertTrue(range1.isOverlappedBy(Range.of(16d, 21d)));
+
+        // touches lower boundary
+        assertTrue(range1.isOverlappedBy(Range.of(10d, 19d)));
+        assertTrue(range1.isOverlappedBy(Range.of(10d, 21d)));
+
+        // touches upper boundary
+        assertTrue(range1.isOverlappedBy(Range.of(11d, 20d)));
+        assertTrue(range1.isOverlappedBy(Range.of(9d, 20d)));
+
+        // negative
+        assertFalse(range1.isOverlappedBy(Range.of(-11d, -18d)));
+
+        // outside range whole range
+        assertTrue(range1.isOverlappedBy(Range.of(9d, 21d)));
+    }
+
+    @Test
+    public void testIsStartedBy() {
+        assertFalse(range1.isStartedBy(null));
+
+        assertFalse(range1.isStartedBy(5d));
+        assertTrue(range1.isStartedBy(10d));
+        assertFalse(range1.isStartedBy(15d));
+        assertFalse(range1.isStartedBy(20d));
+        assertFalse(range1.isStartedBy(25d));
+    }
+
+    @Test
+    public void testIsWithCompareRange() {
+        // all integers are equal
+        final Comparator<Integer> c = (o1, o2) -> 0;
+        Range<Integer> ri = Range.is(10);
+        assertFalse(ri.contains(null), "should not contain null");
+        assertTrue(ri.contains(10), "should contain 10");
+        assertFalse(ri.contains(11), "should not contain 11");
+        ri = Range.is(10, c);
+        assertFalse(ri.contains(null), "should not contain null");
+        assertTrue(ri.contains(10), "should contain 10");
+        assertTrue(ri.contains(11), "should contain 11");
+    }
+
+    @Test
+    public void testOfWithContains() {
+        // all integers are equal
+        final DoubleRange rb = of(-10, 20);
+        assertFalse(rb.contains(null), "should not contain null");
+        assertTrue(rb.contains(10d), "should contain 10");
+        assertTrue(rb.contains(-10d), "should contain -10");
+        assertFalse(rb.contains(21d), "should not contain 21");
+        assertFalse(rb.contains(-11d), "should not contain -11");
+
+        assertThrows(NullPointerException.class, () -> of(null, null));
+    }
+
+    @Test
+    public void testRangeOfChars() {
+        final DoubleRange chars = of('a', 'z');
+        assertTrue(chars.contains((double) 'b'));
+        assertFalse(chars.contains((double) 'B'));
+    }
+
+    @Test
+    public void testSerializing() {
+        SerializationUtils.clone(range1);
+    }
+
+    @Test
+    public void testToString() {
+        assertNotNull(range1.toString());
+
+        final String str = range1.toString();
+        assertEquals("[10.0..20.0]", str);
+        assertEquals("[-20.0..-10.0]", Range.of(-20d, -10d).toString());
+        assertEquals("[-20.0..-10.0]", DoubleRange.of(-20d, -10d).toString());
+    }
+
+    @Test
+    public void testToStringFormat() {
+        final String str = range1.toString("From %1$s to %2$s");
+        assertEquals("From 10.0 to 20.0", str);
+    }
+}
diff --git a/src/test/java/org/apache/commons/lang3/IntegerRangeTest.java b/src/test/java/org/apache/commons/lang3/IntegerRangeTest.java
new file mode 100644
index 000000000..7c894f298
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/IntegerRangeTest.java
@@ -0,0 +1,411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Comparator;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link IntegerRange}.
+ */
+@SuppressWarnings("boxing")
+public class IntegerRangeTest extends AbstractLangTest {
+
+    private static IntegerRange of(final int min, final int max) {
+        return IntegerRange.of(min, max);
+    }
+
+    private static IntegerRange of(final Integer min, final Integer max) {
+        return IntegerRange.of(min, max);
+    }
+
+    private IntegerRange range1;
+
+    private IntegerRange range2;
+
+    private IntegerRange range3;
+
+    private IntegerRange rangeFull;
+
+    @BeforeEach
+    public void setUp() {
+        range1 = of(10, 20);
+        range2 = of(10, 20);
+        range3 = of(-2, -1);
+        rangeFull = of(Integer.MIN_VALUE, Integer.MAX_VALUE);
+    }
+
+    @Test
+    public void testContainsInt() {
+        assertFalse(range1.contains(null));
+
+        assertTrue(rangeFull.contains(Integer.MIN_VALUE));
+        assertTrue(rangeFull.contains(Integer.MAX_VALUE));
+
+        assertFalse(range1.contains(5));
+        assertTrue(range1.contains(10));
+        assertTrue(range1.contains(15));
+        assertTrue(range1.contains(20));
+        assertFalse(range1.contains(25));
+    }
+
+    @Test
+    public void testContainsRange() {
+
+        // null handling
+        assertFalse(range1.containsRange(null));
+
+        // easy inside range
+        assertTrue(range1.containsRange(Range.of(12, 18)));
+        assertTrue(range1.containsRange(of(12, 18)));
+
+        // outside range on each side
+        assertFalse(range1.containsRange(Range.of(32, 45)));
+        assertFalse(range1.containsRange(of(32, 45)));
+        assertFalse(range1.containsRange(Range.of(2, 8)));
+        assertFalse(range1.containsRange(of(2, 8)));
+
+        // equals range
+        assertTrue(range1.containsRange(Range.of(10, 20)));
+        assertTrue(range1.containsRange(of(10, 20)));
+
+        // overlaps
+        assertFalse(range1.containsRange(Range.of(9, 14)));
+        assertFalse(range1.containsRange(of(9, 14)));
+        assertFalse(range1.containsRange(Range.of(16, 21)));
+        assertFalse(range1.containsRange(of(16, 21)));
+
+        // touches lower boundary
+        assertTrue(range1.containsRange(Range.of(10, 19)));
+        assertTrue(range1.containsRange(of(10, 19)));
+        assertFalse(range1.containsRange(Range.of(10, 21)));
+        assertFalse(range1.containsRange(of(10, 21)));
+
+        // touches upper boundary
+        assertTrue(range1.containsRange(Range.of(11, 20)));
+        assertTrue(range1.containsRange(of(11, 20)));
+        assertFalse(range1.containsRange(Range.of(9, 20)));
+        assertFalse(range1.containsRange(of(9, 20)));
+
+        // negative
+        assertFalse(range1.containsRange(Range.of(-11, -18)));
+        assertFalse(range1.containsRange(of(-11, -18)));
+    }
+
+    @Test
+    public void testElementCompareTo() {
+        assertThrows(NullPointerException.class, () -> range1.elementCompareTo(null));
+
+        assertEquals(-1, range1.elementCompareTo(5));
+        assertEquals(0, range1.elementCompareTo(10));
+        assertEquals(0, range1.elementCompareTo(15));
+        assertEquals(0, range1.elementCompareTo(20));
+        assertEquals(1, range1.elementCompareTo(25));
+    }
+
+    @Test
+    public void testEqualsObject() {
+        assertEquals(range1, range1);
+        assertEquals(range1, range2);
+        assertEquals(range2, range2);
+        assertEquals(range1, range1);
+        assertEquals(range2, range2);
+        assertEquals(range3, range3);
+        assertNotEquals(range2, range3);
+        assertNotEquals(null, range2);
+        assertNotEquals("Ni!", range2);
+    }
+
+    @Test
+    public void testFit() {
+        assertEquals(range1.getMinimum(), range1.fit(Integer.MIN_VALUE));
+        assertEquals(range1.getMinimum(), range1.fit(range1.getMinimum()));
+        assertEquals(range1.getMaximum(), range1.fit(Integer.MAX_VALUE));
+        assertEquals(range1.getMaximum(), range1.fit(range1.getMaximum()));
+        assertEquals(15, range1.fit(15));
+    }
+
+    @Test
+    public void testFitNull() {
+        assertThrows(NullPointerException.class, () -> {
+            range1.fit(null);
+        });
+    }
+
+    @Test
+    public void testGetMaximum() {
+        assertEquals(20, (int) range1.getMaximum());
+    }
+
+    @Test
+    public void testGetMinimum() {
+        assertEquals(10, (int) range1.getMinimum());
+    }
+
+    @Test
+    public void testHashCode() {
+        assertEquals(range1.hashCode(), range2.hashCode());
+        assertNotEquals(range1.hashCode(), range3.hashCode());
+
+        assertEquals(range1.hashCode(), range1.hashCode());
+        assertTrue(range1.hashCode() != 0);
+    }
+
+    @Test
+    public void testIntersectionWith() {
+        assertSame(range1, range1.intersectionWith(range1));
+
+        assertEquals(Range.of(10, 15), range1.intersectionWith(Range.of(5, 15)));
+    }
+
+    @Test
+    public void testIntersectionWithNonOverlapping() {
+        assertThrows(IllegalArgumentException.class, () -> range1.intersectionWith(Range.of(0, 9)));
+    }
+
+    @Test
+    public void testIntersectionWithNull() {
+        assertThrows(IllegalArgumentException.class, () -> range1.intersectionWith(null));
+    }
+
+    @Test
+    public void testIsAfter() {
+        assertFalse(range1.isAfter(null));
+
+        assertTrue(range1.isAfter(5));
+        assertFalse(range1.isAfter(10));
+        assertFalse(range1.isAfter(15));
+        assertFalse(range1.isAfter(20));
+        assertFalse(range1.isAfter(25));
+    }
+
+    @Test
+    public void testIsAfterRange() {
+        assertFalse(range1.isAfterRange(null));
+
+        assertTrue(range1.isAfterRange(Range.of(5, 9)));
+
+        assertFalse(range1.isAfterRange(Range.of(5, 10)));
+        assertFalse(range1.isAfterRange(Range.of(5, 20)));
+        assertFalse(range1.isAfterRange(Range.of(5, 25)));
+        assertFalse(range1.isAfterRange(Range.of(15, 25)));
+
+        assertFalse(range1.isAfterRange(Range.of(21, 25)));
+
+        assertFalse(range1.isAfterRange(Range.of(10, 20)));
+    }
+
+    @Test
+    public void testIsBefore() {
+        assertFalse(range1.isBefore(null));
+
+        assertFalse(range1.isBefore(5));
+        assertFalse(range1.isBefore(10));
+        assertFalse(range1.isBefore(15));
+        assertFalse(range1.isBefore(20));
+        assertTrue(range1.isBefore(25));
+    }
+
+    @Test
+    public void testIsBeforeIntegerRange() {
+        assertFalse(range1.isBeforeRange(null));
+
+        assertFalse(range1.isBeforeRange(of(5, 9)));
+
+        assertFalse(range1.isBeforeRange(of(5, 10)));
+        assertFalse(range1.isBeforeRange(of(5, 20)));
+        assertFalse(range1.isBeforeRange(of(5, 25)));
+        assertFalse(range1.isBeforeRange(of(15, 25)));
+
+        assertTrue(range1.isBeforeRange(of(21, 25)));
+
+        assertFalse(range1.isBeforeRange(of(10, 20)));
+    }
+
+    @Test
+    public void testIsBeforeRange() {
+        assertFalse(range1.isBeforeRange(null));
+
+        assertFalse(range1.isBeforeRange(Range.of(5, 9)));
+
+        assertFalse(range1.isBeforeRange(Range.of(5, 10)));
+        assertFalse(range1.isBeforeRange(Range.of(5, 20)));
+        assertFalse(range1.isBeforeRange(Range.of(5, 25)));
+        assertFalse(range1.isBeforeRange(Range.of(15, 25)));
+
+        assertTrue(range1.isBeforeRange(Range.of(21, 25)));
+
+        assertFalse(range1.isBeforeRange(Range.of(10, 20)));
+    }
+
+    @Test
+    public void testIsEndedBy() {
+        assertFalse(range1.isEndedBy(null));
+
+        assertFalse(range1.isEndedBy(5));
+        assertFalse(range1.isEndedBy(10));
+        assertFalse(range1.isEndedBy(15));
+        assertTrue(range1.isEndedBy(20));
+        assertFalse(range1.isEndedBy(25));
+    }
+
+    @Test
+    public void testIsOverlappedByIntegerRange() {
+
+        // null handling
+        assertFalse(range1.isOverlappedBy(null));
+
+        // easy inside range
+        assertTrue(range1.isOverlappedBy(of(12, 18)));
+
+        // outside range on each side
+        assertFalse(range1.isOverlappedBy(of(32, 45)));
+        assertFalse(range1.isOverlappedBy(of(2, 8)));
+
+        // equals range
+        assertTrue(range1.isOverlappedBy(of(10, 20)));
+
+        // overlaps
+        assertTrue(range1.isOverlappedBy(of(9, 14)));
+        assertTrue(range1.isOverlappedBy(of(16, 21)));
+
+        // touches lower boundary
+        assertTrue(range1.isOverlappedBy(of(10, 19)));
+        assertTrue(range1.isOverlappedBy(of(10, 21)));
+
+        // touches upper boundary
+        assertTrue(range1.isOverlappedBy(of(11, 20)));
+        assertTrue(range1.isOverlappedBy(of(9, 20)));
+
+        // negative
+        assertFalse(range1.isOverlappedBy(of(-11, -18)));
+
+        // outside range whole range
+        assertTrue(range1.isOverlappedBy(of(9, 21)));
+    }
+
+    @Test
+    public void testIsOverlappedByRange() {
+
+        // null handling
+        assertFalse(range1.isOverlappedBy(null));
+
+        // easy inside range
+        assertTrue(range1.isOverlappedBy(Range.of(12, 18)));
+
+        // outside range on each side
+        assertFalse(range1.isOverlappedBy(Range.of(32, 45)));
+        assertFalse(range1.isOverlappedBy(Range.of(2, 8)));
+
+        // equals range
+        assertTrue(range1.isOverlappedBy(Range.of(10, 20)));
+
+        // overlaps
+        assertTrue(range1.isOverlappedBy(Range.of(9, 14)));
+        assertTrue(range1.isOverlappedBy(Range.of(16, 21)));
+
+        // touches lower boundary
+        assertTrue(range1.isOverlappedBy(Range.of(10, 19)));
+        assertTrue(range1.isOverlappedBy(Range.of(10, 21)));
+
+        // touches upper boundary
+        assertTrue(range1.isOverlappedBy(Range.of(11, 20)));
+        assertTrue(range1.isOverlappedBy(Range.of(9, 20)));
+
+        // negative
+        assertFalse(range1.isOverlappedBy(Range.of(-11, -18)));
+
+        // outside range whole range
+        assertTrue(range1.isOverlappedBy(Range.of(9, 21)));
+    }
+
+    @Test
+    public void testIsStartedBy() {
+        assertFalse(range1.isStartedBy(null));
+
+        assertFalse(range1.isStartedBy(5));
+        assertTrue(range1.isStartedBy(10));
+        assertFalse(range1.isStartedBy(15));
+        assertFalse(range1.isStartedBy(20));
+        assertFalse(range1.isStartedBy(25));
+    }
+
+    @Test
+    public void testIsWithCompareRange() {
+        // all integers are equal
+        final Comparator<Integer> c = (o1, o2) -> 0;
+        Range<Integer> ri = Range.is(10);
+        assertFalse(ri.contains(null), "should not contain null");
+        assertTrue(ri.contains(10), "should contain 10");
+        assertFalse(ri.contains(11), "should not contain 11");
+        ri = Range.is(10, c);
+        assertFalse(ri.contains(null), "should not contain null");
+        assertTrue(ri.contains(10), "should contain 10");
+        assertTrue(ri.contains(11), "should contain 11");
+    }
+
+    @Test
+    public void testOfWithContains() {
+        // all integers are equal
+        final IntegerRange rb = of(-10, 20);
+        assertFalse(rb.contains(null), "should not contain null");
+        assertTrue(rb.contains(10), "should contain 10");
+        assertTrue(rb.contains(-10), "should contain -10");
+        assertFalse(rb.contains(21), "should not contain 21");
+        assertFalse(rb.contains(-11), "should not contain -11");
+
+        assertThrows(NullPointerException.class, () -> of(null, null));
+    }
+
+    @Test
+    public void testRangeOfChars() {
+        final IntegerRange chars = of('a', 'z');
+        assertTrue(chars.contains((int) 'b'));
+        assertFalse(chars.contains((int) 'B'));
+    }
+
+    @Test
+    public void testSerializing() {
+        SerializationUtils.clone(range1);
+    }
+
+    @Test
+    public void testToString() {
+        assertNotNull(range1.toString());
+
+        final String str = range1.toString();
+        assertEquals("[10..20]", str);
+        assertEquals("[-20..-10]", Range.of(-20, -10).toString());
+    }
+
+    @Test
+    public void testToStringFormat() {
+        final String str = range1.toString("From %1$s to %2$s");
+        assertEquals("From 10 to 20", str);
+    }
+}
diff --git a/src/test/java/org/apache/commons/lang3/LongRangeTest.java b/src/test/java/org/apache/commons/lang3/LongRangeTest.java
new file mode 100644
index 000000000..290ce8a00
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/LongRangeTest.java
@@ -0,0 +1,426 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.lang3;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Comparator;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link LongRange}.
+ */
+@SuppressWarnings("boxing")
+public class LongRangeTest extends AbstractLangTest {
+
+    private static LongRange of(final int min, final int max) {
+        return LongRange.of(min, max);
+    }
+
+    private static LongRange of(final Long min, final Long max) {
+        return LongRange.of(min, max);
+    }
+
+    private LongRange range1;
+
+    private LongRange range2;
+
+    private LongRange range3;
+
+    private LongRange rangeFull;
+
+    @BeforeEach
+    public void setUp() {
+        range1 = of(10, 20);
+        range2 = of(10, 20);
+        range3 = of(-2, -1);
+        rangeFull = of(Long.MIN_VALUE, Long.MAX_VALUE);
+    }
+
+    @Test
+    public void testContainsInt() {
+        assertFalse(range1.contains(null));
+
+        assertFalse(range1.contains(5L));
+        assertTrue(range1.contains(10L));
+        assertTrue(range1.contains(15L));
+        assertTrue(range1.contains(20L));
+        assertFalse(range1.contains(25L));
+    }
+
+    @Test
+    public void testContainsLong() {
+        assertFalse(range1.contains(null));
+
+        assertTrue(rangeFull.contains(Long.MAX_VALUE));
+        assertTrue(rangeFull.contains(Long.MIN_VALUE));
+        assertTrue(rangeFull.contains((long) Integer.MAX_VALUE + 1));
+        assertTrue(rangeFull.contains((long) Integer.MIN_VALUE - 1));
+        assertTrue(rangeFull.contains((long) Integer.MAX_VALUE));
+        assertTrue(rangeFull.contains((long) Integer.MIN_VALUE));
+
+        assertFalse(range1.contains(5L));
+        assertTrue(range1.contains(10L));
+        assertTrue(range1.contains(15L));
+        assertTrue(range1.contains(20L));
+        assertFalse(range1.contains(25L));
+    }
+
+    @Test
+    public void testContainsRange() {
+
+        // null handling
+        assertFalse(range1.containsRange(null));
+
+        // easy inside range
+        assertTrue(range1.containsRange(Range.of(12L, 18L)));
+        assertTrue(range1.containsRange(of(12, 18)));
+
+        // outside range on each side
+        assertFalse(range1.containsRange(Range.of(32L, 45L)));
+        assertFalse(range1.containsRange(of(32, 45)));
+        assertFalse(range1.containsRange(Range.of(2L, 8L)));
+        assertFalse(range1.containsRange(of(2, 8)));
+
+        // equals range
+        assertTrue(range1.containsRange(Range.of(10L, 20L)));
+        assertTrue(range1.containsRange(of(10, 20)));
+
+        // overlaps
+        assertFalse(range1.containsRange(Range.of(9L, 14L)));
+        assertFalse(range1.containsRange(of(9, 14)));
+        assertFalse(range1.containsRange(Range.of(16L, 21L)));
+        assertFalse(range1.containsRange(of(16, 21)));
+
+        // touches lower boundary
+        assertTrue(range1.containsRange(Range.of(10L, 19L)));
+        assertTrue(range1.containsRange(of(10, 19)));
+        assertFalse(range1.containsRange(Range.of(10L, 21L)));
+        assertFalse(range1.containsRange(of(10, 21)));
+
+        // touches upper boundary
+        assertTrue(range1.containsRange(Range.of(11L, 20L)));
+        assertTrue(range1.containsRange(of(11, 20)));
+        assertFalse(range1.containsRange(Range.of(9L, 20L)));
+        assertFalse(range1.containsRange(of(9, 20)));
+
+        // negative
+        assertFalse(range1.containsRange(Range.of(-11L, -18L)));
+        assertFalse(range1.containsRange(of(-11, -18)));
+    }
+
+    @Test
+    public void testElementCompareTo() {
+        assertThrows(NullPointerException.class, () -> range1.elementCompareTo(null));
+
+        assertEquals(-1, range1.elementCompareTo(5L));
+        assertEquals(0, range1.elementCompareTo(10L));
+        assertEquals(0, range1.elementCompareTo(15L));
+        assertEquals(0, range1.elementCompareTo(20L));
+        assertEquals(1, range1.elementCompareTo(25L));
+    }
+
+    @Test
+    public void testEqualsObject() {
+        assertEquals(range1, range1);
+        assertEquals(range1, range2);
+        assertEquals(range2, range2);
+        assertEquals(range1, range1);
+        assertEquals(range2, range2);
+        assertEquals(range3, range3);
+        assertNotEquals(range2, range3);
+        assertNotEquals(null, range2);
+        assertNotEquals("Ni!", range2);
+    }
+
+    @Test
+    public void testFit() {
+        assertEquals(range1.getMinimum(), range1.fit((long) Integer.MIN_VALUE));
+        assertEquals(range1.getMinimum(), range1.fit(range1.getMinimum()));
+        assertEquals(range1.getMaximum(), range1.fit((long) Integer.MAX_VALUE));
+        assertEquals(range1.getMaximum(), range1.fit(range1.getMaximum()));
+        assertEquals(15, range1.fit(15L));
+    }
+
+    @Test
+    public void testFitNull() {
+        assertThrows(NullPointerException.class, () -> {
+            range1.fit(null);
+        });
+    }
+
+    @Test
+    public void testGetMaximum() {
+        assertEquals(20, range1.getMaximum());
+    }
+
+    @Test
+    public void testGetMinimum() {
+        assertEquals(10, range1.getMinimum());
+    }
+
+    @Test
+    public void testHashCode() {
+        assertEquals(range1.hashCode(), range2.hashCode());
+        assertNotEquals(range1.hashCode(), range3.hashCode());
+
+        assertEquals(range1.hashCode(), range1.hashCode());
+        assertTrue(range1.hashCode() != 0);
+    }
+
+    @Test
+    public void testIntersectionWith() {
+        assertSame(range1, range1.intersectionWith(range1));
+
+        assertEquals(Range.of(10L, 15L), range1.intersectionWith(Range.of(5L, 15L)));
+    }
+
+    @Test
+    public void testIntersectionWithNonOverlapping() {
+        assertThrows(IllegalArgumentException.class, () -> range1.intersectionWith(Range.of(0L, 9L)));
+    }
+
+    @Test
+    public void testIntersectionWithNull() {
+        assertThrows(IllegalArgumentException.class, () -> range1.intersectionWith(null));
+    }
+
+    @Test
+    public void testIsAfter() {
+        assertFalse(range1.isAfter(null));
+
+        assertTrue(range1.isAfter(5L));
+        assertFalse(range1.isAfter(10L));
+        assertFalse(range1.isAfter(15L));
+        assertFalse(range1.isAfter(20L));
+        assertFalse(range1.isAfter(25L));
+    }
+
+    @Test
+    public void testIsAfterRange() {
+        assertFalse(range1.isAfterRange(null));
+
+        assertTrue(range1.isAfterRange(Range.of(5L, 9L)));
+
+        assertFalse(range1.isAfterRange(Range.of(5L, 10L)));
+        assertFalse(range1.isAfterRange(Range.of(5L, 20L)));
+        assertFalse(range1.isAfterRange(Range.of(5L, 25L)));
+        assertFalse(range1.isAfterRange(Range.of(15L, 25L)));
+
+        assertFalse(range1.isAfterRange(Range.of(21L, 25L)));
+
+        assertFalse(range1.isAfterRange(Range.of(10L, 20L)));
+    }
+
+    @Test
+    public void testIsBefore() {
+        assertFalse(range1.isBefore(null));
+
+        assertFalse(range1.isBefore(5L));
+        assertFalse(range1.isBefore(10L));
+        assertFalse(range1.isBefore(15L));
+        assertFalse(range1.isBefore(20L));
+        assertTrue(range1.isBefore(25L));
+    }
+
+    @Test
+    public void testIsBeforeIntegerRange() {
+        assertFalse(range1.isBeforeRange(null));
+
+        assertFalse(range1.isBeforeRange(of(5, 9)));
+
+        assertFalse(range1.isBeforeRange(of(5, 10)));
+        assertFalse(range1.isBeforeRange(of(5, 20)));
+        assertFalse(range1.isBeforeRange(of(5, 25)));
+        assertFalse(range1.isBeforeRange(of(15, 25)));
+
+        assertTrue(range1.isBeforeRange(of(21, 25)));
+
+        assertFalse(range1.isBeforeRange(of(10, 20)));
+    }
+
+    @Test
+    public void testIsBeforeRange() {
+        assertFalse(range1.isBeforeRange(null));
+
+        assertFalse(range1.isBeforeRange(Range.of(5L, 9L)));
+
+        assertFalse(range1.isBeforeRange(Range.of(5L, 10L)));
+        assertFalse(range1.isBeforeRange(Range.of(5L, 20L)));
+        assertFalse(range1.isBeforeRange(Range.of(5L, 25L)));
+        assertFalse(range1.isBeforeRange(Range.of(15L, 25L)));
+
+        assertTrue(range1.isBeforeRange(Range.of(21L, 25L)));
+
+        assertFalse(range1.isBeforeRange(Range.of(10L, 20L)));
+    }
+
+    @Test
+    public void testIsEndedBy() {
+        assertFalse(range1.isEndedBy(null));
+
+        assertFalse(range1.isEndedBy(5L));
+        assertFalse(range1.isEndedBy(10L));
+        assertFalse(range1.isEndedBy(15L));
+        assertTrue(range1.isEndedBy(20L));
+        assertFalse(range1.isEndedBy(25L));
+    }
+
+    @Test
+    public void testIsOverlappedByIntegerRange() {
+
+        // null handling
+        assertFalse(range1.isOverlappedBy(null));
+
+        // easy inside range
+        assertTrue(range1.isOverlappedBy(of(12, 18)));
+
+        // outside range on each side
+        assertFalse(range1.isOverlappedBy(of(32, 45)));
+        assertFalse(range1.isOverlappedBy(of(2, 8)));
+
+        // equals range
+        assertTrue(range1.isOverlappedBy(of(10, 20)));
+
+        // overlaps
+        assertTrue(range1.isOverlappedBy(of(9, 14)));
+        assertTrue(range1.isOverlappedBy(of(16, 21)));
+
+        // touches lower boundary
+        assertTrue(range1.isOverlappedBy(of(10, 19)));
+        assertTrue(range1.isOverlappedBy(of(10, 21)));
+
+        // touches upper boundary
+        assertTrue(range1.isOverlappedBy(of(11, 20)));
+        assertTrue(range1.isOverlappedBy(of(9, 20)));
+
+        // negative
+        assertFalse(range1.isOverlappedBy(of(-11, -18)));
+
+        // outside range whole range
+        assertTrue(range1.isOverlappedBy(of(9, 21)));
+    }
+
+    @Test
+    public void testIsOverlappedByRange() {
+
+        // null handling
+        assertFalse(range1.isOverlappedBy(null));
+
+        // easy inside range
+        assertTrue(range1.isOverlappedBy(Range.of(12L, 18L)));
+
+        // outside range on each side
+        assertFalse(range1.isOverlappedBy(Range.of(32L, 45L)));
+        assertFalse(range1.isOverlappedBy(Range.of(2L, 8L)));
+
+        // equals range
+        assertTrue(range1.isOverlappedBy(Range.of(10L, 20L)));
+
+        // overlaps
+        assertTrue(range1.isOverlappedBy(Range.of(9L, 14L)));
+        assertTrue(range1.isOverlappedBy(Range.of(16L, 21L)));
+
+        // touches lower boundary
+        assertTrue(range1.isOverlappedBy(Range.of(10L, 19L)));
+        assertTrue(range1.isOverlappedBy(Range.of(10L, 21L)));
+
+        // touches upper boundary
+        assertTrue(range1.isOverlappedBy(Range.of(11L, 20L)));
+        assertTrue(range1.isOverlappedBy(Range.of(9L, 20L)));
+
+        // negative
+        assertFalse(range1.isOverlappedBy(Range.of(-11L, -18L)));
+
+        // outside range whole range
+        assertTrue(range1.isOverlappedBy(Range.of(9L, 21L)));
+    }
+
+    @Test
+    public void testIsStartedBy() {
+        assertFalse(range1.isStartedBy(null));
+
+        assertFalse(range1.isStartedBy(5L));
+        assertTrue(range1.isStartedBy(10L));
+        assertFalse(range1.isStartedBy(15L));
+        assertFalse(range1.isStartedBy(20L));
+        assertFalse(range1.isStartedBy(25L));
+    }
+
+    @Test
+    public void testIsWithCompareRange() {
+        // all integers are equal
+        final Comparator<Integer> c = (o1, o2) -> 0;
+        Range<Integer> ri = Range.is(10);
+        assertFalse(ri.contains(null), "should not contain null");
+        assertTrue(ri.contains(10), "should contain 10");
+        assertFalse(ri.contains(11), "should not contain 11");
+        ri = Range.is(10, c);
+        assertFalse(ri.contains(null), "should not contain null");
+        assertTrue(ri.contains(10), "should contain 10");
+        assertTrue(ri.contains(11), "should contain 11");
+    }
+
+    @Test
+    public void testOfWithContains() {
+        // all integers are equal
+        final LongRange rb = of(-10, 20);
+        assertFalse(rb.contains(null), "should not contain null");
+        assertTrue(rb.contains(10L), "should contain 10");
+        assertTrue(rb.contains(-10L), "should contain -10");
+        assertFalse(rb.contains(21L), "should not contain 21");
+        assertFalse(rb.contains(-11L), "should not contain -11");
+
+        assertThrows(NullPointerException.class, () -> of(null, null));
+    }
+
+    @Test
+    public void testRangeOfChars() {
+        final LongRange chars = of((long) 'a', (long) 'z');
+        assertTrue(chars.contains((long) 'b'));
+        assertFalse(chars.contains((long) 'B'));
+    }
+
+    @Test
+    public void testSerializing() {
+        SerializationUtils.clone(range1);
+    }
+
+    @Test
+    public void testToString() {
+        assertNotNull(range1.toString());
+
+        final String str = range1.toString();
+        assertEquals("[10..20]", str);
+        assertEquals("[-20..-10]", Range.of(-20, -10).toString());
+    }
+
+    @Test
+    public void testToStringFormat() {
+        final String str = range1.toString("From %1$s to %2$s");
+        assertEquals("From 10 to 20", str);
+    }
+}
diff --git a/src/test/java/org/apache/commons/lang3/RangeTest.java b/src/test/java/org/apache/commons/lang3/RangeTest.java
index 4ed18cdf4..8b477c354 100644
--- a/src/test/java/org/apache/commons/lang3/RangeTest.java
+++ b/src/test/java/org/apache/commons/lang3/RangeTest.java
@@ -53,23 +53,20 @@ public class RangeTest extends AbstractLangTest {
     private Range<Byte> byteRange2;
     private Range<Byte> byteRange3;
     private Range<Double> doubleRange;
-
     private Range<Float> floatRange;
-
     private Range<Integer> intRange;
-
     private Range<Long> longRange;
 
     @BeforeEach
     public void setUp() {
-        byteRange = Range.between((byte) 0, (byte) 5);
-        byteRange2 = Range.between((byte) 0, (byte) 5);
-        byteRange3 = Range.between((byte) 0, (byte) 10);
-
-        intRange = Range.between(10, 20);
-        longRange = Range.between(10L, 20L);
-        floatRange = Range.between((float) 10, (float) 20);
-        doubleRange = Range.between((double) 10, (double) 20);
+        byteRange = Range.of((byte) 0, (byte) 5);
+        byteRange2 = Range.of((byte) 0, (byte) 5);
+        byteRange3 = Range.of((byte) 0, (byte) 10);
+
+        intRange = Range.of(10, 20);
+        longRange = Range.of(10L, 20L);
+        floatRange = Range.of((float) 10, (float) 20);
+        doubleRange = Range.of((double) 10, (double) 20);
     }
 
     @Test
@@ -102,9 +99,40 @@ public class RangeTest extends AbstractLangTest {
         assertFalse(rbstr.contains("houses"), "should not contain houses");
         assertFalse(rbstr.contains(""), "should not contain ''");
 
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> Range.between(null, null, lengthComp));
+        assertThrows(NullPointerException.class, () -> Range.between(null, null, lengthComp));
+    }
+
+    @Test
+    public void testOfWithCompare() {
+        // all integers are equal
+        final Comparator<Integer> c = (o1, o2) -> 0;
+        final Comparator<String> lengthComp = Comparator.comparingInt(String::length);
+        Range<Integer> rb = Range.of(-10, 20);
+        assertFalse(rb.contains(null), "should not contain null");
+        assertTrue(rb.contains(10), "should contain 10");
+        assertTrue(rb.contains(-10), "should contain -10");
+        assertFalse(rb.contains(21), "should not contain 21");
+        assertFalse(rb.contains(-11), "should not contain -11");
+        rb = Range.of(-10, 20, c);
+        assertFalse(rb.contains(null), "should not contain null");
+        assertTrue(rb.contains(10), "should contain 10");
+        assertTrue(rb.contains(-10), "should contain -10");
+        assertTrue(rb.contains(21), "should contain 21");
+        assertTrue(rb.contains(-11), "should contain -11");
+        Range<String> rbstr = Range.of("house", "i");
+        assertFalse(rbstr.contains(null), "should not contain null");
+        assertTrue(rbstr.contains("house"), "should contain house");
+        assertTrue(rbstr.contains("i"), "should contain i");
+        assertFalse(rbstr.contains("hose"), "should not contain hose");
+        assertFalse(rbstr.contains("ice"), "should not contain ice");
+        rbstr = Range.of("house", "i", lengthComp);
+        assertFalse(rbstr.contains(null), "should not contain null");
+        assertTrue(rbstr.contains("house"), "should contain house");
+        assertTrue(rbstr.contains("i"), "should contain i");
+        assertFalse(rbstr.contains("houses"), "should not contain houses");
+        assertFalse(rbstr.contains(""), "should not contain ''");
+
+        assertThrows(NullPointerException.class, () -> Range.of(null, null, lengthComp));
     }
 
     @SuppressWarnings({"rawtypes", "unchecked"})