Thursday, May 7, 2026

pass Statement in Python

The pass statement in Python is a simple yet powerful tool that acts as a placeholder in your code. It performs no action when executed, but ensures that your program remains syntactically correct. This makes it especially useful when you want to define the structure of your code without implementing the logic right away.

Python pass statement

  • The pass statement is a null operation, when it is executed, nothing happens.
  • It is often used in places where a statement is required syntactically, but you don’t want to perform any operation yet.
  • Common use cases include empty functions, classes, loops, or conditional blocks that you plan to implement later.

Using pass in a Loop

If you have an array of numbers and you want to display only those numbers which are less than 100.

numbers = [89, 102, 45, 234, 67, 10, 333, 32]
for num in numbers:
  if num > 100:
    #do nothing
    pass
  else:
    print('number is', num)

Output

number is 89
number is 45
number is 67
number is 10
number is 32

In the above example, if-else statement checks whether each number in the list is greater than 100 or not. In case it is then you do nothing and that is explicitly indicated using the pass statement.

In most cases, you can write Python code without explicitly using the pass statement. However, including it often improves readability and makes your intent clear to other developers. The pass statement ensures that your code remains syntactically valid and prevents errors such as IndentationError when leaving a block empty.

Using pass in Classes

Suppose there is a Person class where you have a method to display Person data and retrieve person data. You are planning to implement retrieveData() method later, that will fetch Person details from DB, so you keep the method in place as a TODO reminder.

class Person:
    def __init__(self, name, age):
        print('init called')
        self.name = name
        self.age = age

    def display(self):
        print('in display')
        print("Name-", self.name)
        print("Age-", self.age)

    def retrieveData(self):
        #TODO has to be implemented to fetch details from DB

person = Person('John', 40)
person.display()

But running this class gives an error “IndentationError: expected an indented block

In such case adding pass statement with unimplemented method ensures that IndentationError is not thrown.

    def retrieveData(self):
        #TODO has to be implemented to fetch details from DB
        pass

So, you can see that Python pass statement can be used where you need a place holder be it with in a for loop, while loop or if-else statement, after def or even after class and in exception handling too.

pass statement with class Inheritance

If you are creating a user‑defined exception class by extending Python’s built‑in Exception class without adding new behavior, you can simply use pass statement.

class MyException(Exception):
    pass

pass Statement in Exception Handling

If you want to catch a specific exception but don't want to take any action after catching it, you can use pass statement to express that intent.

numbers = [89, 102, 0, 234, 67, 10, 333, 32]
for num in numbers:
    try:
        result = 1000/num
        print('Result is',result)
    except ZeroDivisionError:
        print('Division by Zero')
        result = 0
    except AttributeError:
        pass

That's all for this topic pass Statement in Python. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. Python assert Statement
  2. Python Exception Handling Tutorial
  3. Global Keyword in Python With Examples
  4. Abstraction in Python
  5. Difference Between Function and Method in Python

You may also like-

  1. Comparing Two Strings in Python
  2. Namespace And Variable Scope in Python
  3. Passing Object of The Class as Parameter in Python
  4. Python Program to Find Factorial of a Number
  5. What Are JVM, JRE And JDK in Java
  6. Java Program to Detect And Remove Loop in a Linked List
  7. How to Read Input From Console in Java
  8. Word Count MapReduce Program in Hadoop

Java split() Method - Splitting a String

The split() method in Java String class is used to split the string into one or more substring based on the given regular expression.

Java split() method has 2 variants-

  1. split(String regex)
    • Splits this string around matches of the given regular expression.
    • Returns an array of substrings of this string that matches the given expression.
  2. split(String regex, int limit)
    • The limit parameter controls the number of times the regex pattern is applied
    • If the limit is greater than zero then the pattern will be applied at most limit - 1 times, and the resulting array will have at most limit elements.
    • The last element of the array contains the remaining part of the string.

String's split() method examples

  1. If you have a string where one (or more) spaces are used and you want to split this String around those spaces. Here passed regex "\\s+" means one or more spaces.
    public class StringSearch {
     public static void main(String[] args) {
      String str1 = "split example    program";
      String[] strArray = str1.split("\\s+");
      System.out.println("Words in array- " + strArray.length);
      for(String w : strArray){
       System.out.println("words - " + w);
      }
     }
    }
    

    Output

    Words in array- 3
    words - split
    words - example
    words – program
    
  2. If you have a date in dd/mm/yyyy format and you want to split this date String into day, month and year.
    public class StringSearch {
     public static void main(String[] args) {
      String date = "20/01/2016";
      String[] dateArr = date.split("/");
      System.out.println("" + dateArr.length);
      System.out.println("day " + dateArr[0] + " Month " + dateArr[1] +
        " Year " + dateArr[2]);
     }
    }
    

    Output

    3
    day 20 Month 01 Year 2016
    

Using split() method with limit argument

Suppose you just want the day part of the date then you can use the split() method which also passes limit as argument-

public class StringSearch {

 public static void main(String[] args) {
  String date = "20/01/2016";
  String[] dateArr = date.split("/", 2);
  System.out.println("" + dateArr.length);
  System.out.println("day " + dateArr[0]);
 }
}

Output

2
day 20

That's all for this topic Java split() Method - Splitting a String. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Basics Tutorial Page


Related topics

  1. String Comparison in Java equals(), compareTo(), startsWith() Methods
  2. Java String substring() Method - Getting Substring
  3. String Vs StringBuffer Vs StringBuilder in Java
  4. Find All Permutations of a Given String Java Program
  5. Java String Interview Questions And Answers

You may also like-

  1. Count Number of Words in a String Java Program
  2. Java Program to Find The Longest Palindrome in a Given String
  3. Multi-Catch Statement in Java Exception Handling
  4. final Vs finally Vs finalize in Java
  5. Java Pass by Value or Pass by Reference
  6. finalize() Method in Java
  7. Difference Between HashMap And Hashtable in Java
  8. Callable and Future in Java With Examples

Multiple Catch Blocks in Java Exception Handling

Multiple catch blocks in Java allow developers to handle different types of exceptions that may occur within a single try block. When code inside the try block throws an exception, the JVM inspects each catch clause in order. The first block whose exception type matches the thrown exception is executed, and once a matching catch block runs, the remaining ones are skipped. Execution then continues after the entire try-catch block.

This mechanism of multiple catch blocks is especially useful when a program can fail in multiple ways, for example, due to invalid array access or arithmetic errors. By defining multiple catch blocks, you can provide tailored exception handling logic for each exception type, which improves code clarity and robustness.

👉 Since Java 7, developers also have the option to catch multiple exceptions in one catch block, which eliminates the duplicated code. Refer Multi catch statement in Java to read more about it.

Multiple catch blocks Java example

In this program there is an array with only one element which is zero. From main method when calculateValue method is called a parameter is passed which is used as an index of the array.

First time 0 is passed which will mean divide by a[0]. Since the value at that index is 0 thus it will result in divide by 0 and ArithmeticException will be thrown.

Next time 2 is passed but array has only one element so trying to access a[2] will result in ArrayIndexOutOfBoundsException.

In the code there are multiple catch blocks and both of these exceptions will be caught by separate catch blocks.

public class MultipleCatchDemo {
  private void calculateValue(int i){
    int a[] = {0};
    try{
      int b = 7/a[i];
    }catch(ArithmeticException aExp){
      aExp.printStackTrace();
    }catch(ArrayIndexOutOfBoundsException aiExp){
      aiExp.printStackTrace();
    }
  }

  public static void main(String[] args) {
    MultipleCatchDemo mcDemo = new MultipleCatchDemo();
    mcDemo.calculateValue(0);
    mcDemo.calculateValue(2);
  }
}

Output

java.lang.ArithmeticException: / by zero
 at org.netjs.examples.impl.MultipleCatchDemo.calculateValue(MultipleCatchDemo.java:11)
 at org.netjs.examples.impl.MultipleCatchDemo.main(MultipleCatchDemo.java:21)
java.lang.ArrayIndexOutOfBoundsException: 2
 at org.netjs.examples.impl.MultipleCatchDemo.calculateValue(MultipleCatchDemo.java:11)
 at org.netjs.examples.impl.MultipleCatchDemo.main(MultipleCatchDemo.java:22)

⛔Restrictions with Multiple catch blocks in Java

When using multiple catch blocks in Java, it is crucial to maintain the correct order of exception handling. Which means a catch block that catches a subclass of an exception must come before the catch clause that handles its exception super class.

If the superclass catch block is placed first, it will intercept all exceptions of that type, including its subclasses, making the subclass catch block unreachable. Since Java treats unreachable code as a compilation error, this ordering mistake will prevent your program from compiling.

For example, with in the Java exception handling hierarchy Exception class is super class and ArithmeticException is the child class so catch block for Exception class will catch an ArithmeticException too. Thus placing the catch block for Exception class before the catch block for ArithmeticException would mean that the catch block for ArithmeticException is never reached.

try {
    int result = 10 / 0; // ArithmeticException
} catch (Exception e) {
    System.out.println("Generic exception caught.");
} catch (ArithmeticException e) { // Unreachable Catch Block- Causes compile time error
    System.out.println("Arithmetic exception caught.");
}

In the same example as used above, if an additional catch block for Exception is placed at the beginning of the sequence, that will result in compiler error.

multiple catch blocks

That's all for this topic Multiple Catch Blocks in Java Exception Handling. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Best Practices For Exception Handling in Java
  2. Creating Custom Exception Class in Java
  3. throws Keyword in Java Exception Handling
  4. Try-With-Resources in Java With Examples
  5. Java Exception Handling Interview Questions And Answers

You may also like-

  1. Difference Between Abstract Class And Interface in Java
  2. Inheritance in Java
  3. How to Loop Through a Map in Java
  4. Fail-Fast Vs Fail-Safe Iterator in Java
  5. Difference Between Thread And Process in Java
  6. Thread Priority in Java Multi-Threading
  7. Why wait(), notify() And notifyAll() Methods Are in Object Class And Not in Thread Class
  8. Varargs (Variable-length Arguments) in Java

Wednesday, May 6, 2026

Python continue Statement With Examples

The Python continue Statement is a loop control keyword used inside for and while loops to skip the rest of the current iteration and jump directly to the next cycle of the loop. When continue is encountered, Python immediately transfers control back to the beginning of the loop, ignoring any statements that follow within that iteration.

The common use case for continue statement is to pair it with if condition with in the loop to selectively skip certain iterations. When the condition is true the continue statement is executed resulting in next iteration of the loop.

continue statement Python examples

1. Using continue statement with for loop in Python. In the example a for loop in range 1..10 is executed and it prints only odd numbers.

for i in range(1, 10):
    # Completely divisble by 2 means even number
    # in that case continue with next iteration
    if i%2 == 0:
        continue
    print(i)

print('after loop')

Output

1
3
5
7
9
after loop

2. Using continue statement with while loop in Python. In the example while loop is iterated to print numbers 1..10 except numbers 5 and 6. In that case you can have an if condition to continue to next iteration when i is greater than 4 and less than 7.

i = 0
while i < 10:
    i += 1
    if i > 4 and i < 7:
        continue
    print(i)

Output

1
2
3
4
7
8
9
10

3. Here is another example of using continue statement with infinite while loop. In the example there is an infinite while loop that is used to prompt user for an input. Condition here is that entered number should be greater than 10, if entered number is not greater than 10 then continue the loop else break out of the loop.

while True:
    num = int(input("Enter a number greater than 10: "))
    # condition to continue loop
    if num < 10:
        print("Please enter a number greater than 10...")
        continue
    else:
        break

print("Entered number is", num)

Output

Enter a number greater than 10: 5
Please enter a number greater than 10...
Enter a number greater than 10: 16
Entered number is 16

That's all for this topic Python continue Statement With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. Python break Statement With Examples
  2. Python return Statement With Examples
  3. pass Statement in Python
  4. Encapsulation in Python
  5. Namespace And Variable Scope in Python

You may also like-

  1. Constructor in Python - __init__() function
  2. Strings in Python With Method Examples
  3. List in Python With Examples
  4. What Are JVM, JRE And JDK in Java
  5. Equality And Relational Operators in Java
  6. Lambda Expressions in Java 8
  7. What is Hadoop Distributed File System (HDFS)
  8. Circular Dependency in Spring Framework

Method Overloading in Java

Method Overloading in Java occurs when two or more methods in the same class (or in a parent‑child class hierarchy) share the same method name but differ in their parameter list, either by the number of parameters or their types.

For example:

void test(int a, int b) { ... }
void test(float a, int b) { ... }
void test(int a, int b, int c) { ... }

Here, the method name test is reused, but the parameter signatures differ, making them valid overloaded methods.


Method Overloading and Polymorphism

Method overloading is a classic example of compile‑time polymorphism (also called static polymorphism) in Java. The compiler determines which overloaded method to invoke based on the method signature at compile time, ensuring type safety and performance.

How Java Decides Which Overloaded Method to Call

When you invoke an overloaded method, the Java compiler determines which version to execute at compile time itself. This resolution process is based on the method signature, specifically the number and types of parameters provided in the call.

  • Types of the parameters- For example test(int a, int b) and test(float a, int b). In these overloaded methods name is same but the types of the method parameters differ.
  • Number of the parameters- For example test(int a, int b) and test(int a, int b, int c). In these overloaded methods name is same but the number of parameters is different.

Please note that return type of the methods may be different but that alone is not sufficient to determine the method that has to be called.

Examples of method overloading in Java

1. Method overloading example with methods having different types of parameters. In the Java example there are two overloaded methods, one having two int parameters where as the other method has one int and one double parameter.

public class OverloadingExample {
 // overloaded Method
 void overloadedMethod(int i, int j){
  System.out.println("In overloadedMethod with both int parameters- " + i);
 }
 
 // overloaded Method
 void overloadedMethod(int i, double j){
  System.out.println("In overloadedMethod with int and double parameters " + i + " " + j);
 }
 
 
 public static void main(String args[]){ 
  OverloadingExample obj = new OverloadingExample();
  obj.overloadedMethod(5, 7);
  obj.overloadedMethod(5, 103.78);
 }
}

Output

In overloadedMethod with both int parameters- 5
In overloadedMethod with int and double parameters 5 103.78

2. Method overloading example with methods having different number of parameters. In the Java example there are two overloaded methods, one having a single int parameter where as the other method has one int and one String parameter.

public class OverloadingExample {
 // overloaded Method
 void overloadedMethod(int i){
  System.out.println("In overloadedMethod with int parameter- " + i);
 }
 
 // overloaded Method
 void overloadedMethod(int i, String s){
  System.out.println("In overloadedMethod with int and string parameters- " + i + " " + s);
 }
 
 
 public static void main(String args[]){ 
  OverloadingExample obj = new OverloadingExample();
  obj.overloadedMethod(5);
  obj.overloadedMethod(5, "Test");
 }
}

Output

In overloadedMethod with int parameter- 5
In overloadedMethod with int and string parameters- 5 Test

Method overloading in Java and automatic type conversion

In Java, automatic type promotion may happen and it does have a role in how overloaded methods are called, let's see it with an example to have clarity.

public class OverloadingExample {
 // overloaded Method with 1 int param
 void overloadedMethod(int i){
  System.out.println("In overloadedMethod with one int parameter " + i);
 }
 
 // overloaded Method with 2 double params
 void overloadedMethod(double i, double j){
  System.out.println("In overloadedMethod with 2 double parameters " + i + " " + j);
 }
 
 
 public static void main(String args[]){ 
  OverloadingExample obj = new OverloadingExample();
  obj.overloadedMethod(5);
  obj.overloadedMethod(5.7, 103.78);
  obj.overloadedMethod(5, 10);
 }
}

Here notice the third call to the method
obj.overloadedMethod(5, 10);

It has 2 int params, but there is no overloaded method with 2 int params, in this case automatic type conversion happens and java promotes these 2 int parameters to double and calls overloadedMethod(double i, double j) instead.

Method Overloading in Java and Inheritance

In Java, when dealing with a parent‑child relationship (inheritance), the distinction between method overloading and method overriding becomes important.

  • Method Overriding occurs when the child class defines a method with the same name, same parameter list (number and types), and same return type (or a covariant type) as the parent class. In this case, the child's method overrides the parent's implementation, and the decision of which method to call is made at runtime (dynamic polymorphism).
  • Method Overloading, on the other hand, happens when the child class defines a method with the same name but a different parameter list (either in number or types) compared to the parent class. Here, the compiler treats them as distinct methods, and the resolution is done at compile time (static polymorphism).
class Parent {
 private int i;
 // Constructor
 Parent(int i){
  this.i = i;
 }
 // Method with no param
 public void dispayData(){
  System.out.println("Value of i " + i);
 } 
}

class Child extends Parent{
 private int j;
 // Constructor
 Child(int i, int j){
  // Calling parent class constructor
  super(i);
  this.j = j;
 }
 // Overloaded Method with String param
 public void dispayData(String showMsg){
  System.out.println(showMsg + "Value of j " + j);
 } 
}

class OverloadDemo{
 public static void main(String args[]){ 
  Child childObj = new Child(5, 10);
  // This call will invoke the child class method
  childObj.dispayData("in Child class displayData ");
  // This call will invoke the parent class method
  childObj.dispayData();
 }
}

Output of the program would be

in Child class displayData Value of j 10
Value of i 5

Benefits of Method Overloading

One of the biggest advantages of Method Overloading in Java is improved readability and code maintainability. Without overloading, developers would need to create multiple methods with different names for similar functionality, which quickly becomes confusing and clutters the code.

  1. Cleaner Code: Instead of writing separate methods like addInt(int a, int b) or addLong(long a, long b), you can simply use one method name add() with different parameter signatures.
  2. Improved Readability: Overloading allows logically related operations to share the same name, making the code easier to understand at a glance.
  3. Polymorphism Support: Method overloading is a form of compile‑time polymorphism, enabling the compiler to choose the correct method based on the arguments passed.

That's all for this topic Method Overloading in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Basics Tutorial Page


Related Topics

  1. Method Overriding in Java
  2. Polymorphism in Java
  3. Inheritance in Java
  4. Difference Between Encapsulation And Abstraction in Java
  5. Java OOP Interview Questions And Answers

You may also like-

  1. Object Creation Using new Operator in Java
  2. final Keyword in Java With Examples
  3. Java Abstract Class and Abstract Method
  4. Inter-thread Communication Using wait(), notify() And notifyAll() in Java
  5. How HashMap Works Internally in Java
  6. final Vs finally Vs finalize in Java
  7. Method Overloading in Python
  8. Dependency Injection in Spring Framework

StringBuilder Class in Java With Examples

The StringBuilder class in Java, introduced in Java 5, is a mutable (modifiable) sequence of characters just like StringBuffer class. Unlike the immutable String class in Java, where every modification creates a new object, StringBuilder allows in‑place changes to its content and length through various method calls. This makes it ideal for scenarios involving frequent string modifications.

Internally, a StringBuilder class object works like a dynamic, variable‑length array of characters.

StringBuilder vs StringBuffer

Both StringBuilder and StringBuffer share a similar API, but the key difference lies in thread safety:

  • StringBuffer is synchronized and thread‑safe, making it suitable for multi‑threaded environments.
  • StringBuilder is not thread‑safe, but this trade‑off delivers significantly better performance in single‑threaded applications.

As per the official Java documentation– StringBuilder class is designed for use as a drop-in replacement for StringBuffer in places where the string buffer was being used by a single thread (as is generally the case). Where possible, it is recommended that this class be used in preference to StringBuffer as it will be faster under most implementations.


Constructors in Java StringBuilder class

  • StringBuilder()- Creates an empty builder with a default capacity of 16.
  • StringBuilder(CharSequence seq)- Constructs a string builder that contains the same characters as the specified CharSequence.
  • StringBuilder(int capacity)- Creates an empty builder with the specified initial capacity.
  • StringBuilder(String str)- Constructs a string builder initialized to the contents of the specified string.

Tuesday, May 5, 2026

CopyOnWriteArrayList in Java With Examples

This post talks about CopyOnWriteArrayList in Java which is part of the java.util.concurrent package. It is a specialized thread safe implementation of the List interface.


Synchronized List options in Java

Though we have an option to synchronize the collections like List or Set using Collections.synchronizedList() or Collections.synchronizedSet() methods of the Collections class but there is a drawback to this synchronization; very poor performance because-

  • The entire collection is locked during access using a single lock.
  • Only a single thread can access it at a given time leading to significant performance bottlenecks under heavy concurrency.

Java also has a legacy Vector class as a thread-safe alternative to List but that thread safety is achieved by synchronizing all the methods of the Vector class, which again results in poor performance.

CopyOnWriteArrayList in Java

Java 5 onwards, CopyOnWriteArrayList is introduced as a thread-safe variant of ArrayList. It is designed for concurrent access from multiple threads. CopyOnWriteArrayList provides a thread-safe alternative for ArrayList, same way ConcurrentHashMap provides a thread-safe alternative for HashMap and CopyOnWriteArraySet for HashSet.

Thread safety in CopyOnWriteArrayList

Thread safety in CopyOnWriteArrayList lies in its "copy-on-write" strategy:

  • Every mutative operation (add, set, remove) creates a new copy of the underlying array.
  • Readers always see a consistent snapshot of the list, unaffected by concurrent modifications.
  • This ensures lock-free reads and predictable iteration behavior.

You may argue that this way of creating a fresh copy whenever any mutative operation is performed must be very costly. Yes it is, that is why using CopyOnWriteArrayList provides better performance in use cases with frequent reads but infrequent writes.

That brings us to the second point "snapshot style" iterator in CopyOnWriteArrayList.

CopyOnWriteArrayList has a fail-safe iterator

The iterator returned by Java CopyOnWriteArrayList is fail-safe, it uses a reference to the state of the array at the point that the iterator was created. You know by now any mutation will result in a fresh copy of the underlying array. Thus the array that the iterator has a reference to never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException.

The iterator will not reflect additions, removals, or changes to the list since the iterator was created thus it is also known as "snapshot style" iterator.

Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.

Since iterator is not affected by the mutations thus multiple threads can iterate the collection without interference from one another or from threads wanting to modify the collection.

Java CopyOnWriteArrayList constructors

  • CopyOnWriteArrayList()- This constructor Creates an empty list.
  • CopyOnWriteArrayList​(E[] toCopyIn)- Creates a list holding a copy of the given array.
  • CopyOnWriteArrayList​(Collection<? extends E> c)- Creates a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.

CopyOnWriteArrayList Java Example

Let's see a simple Java example creating CopyOnWriteArrayList and adding elements to it.

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteALDemo {
  public static void main(String[] args) {
    List<String> numList = new CopyOnWriteArrayList<String>();
    numList.add("1");
    numList.add("2");
    numList.add("3");
    numList.add("4");
    // Displaying CopyOnWriteArrayList elements
    for(String num : numList){
      System.out.println("Number- " + num);
    }
  }
}

Output

Number- 1
Number- 2
Number- 3
Number- 4

Java CopyOnWriteArrayList iterator Example

Let's see "snapshot style" iterator concept of CopyOnWriteArrayList in Java with an example.

First let's use ArrayList with 2 threads accessing it concurrently. One of the thread tries to structurally modified the ArrayList while second thread is iterating it. This should result in a ConcurrentModificationException.

public class FailFastDemo {
  public static void main(String[] args) {
    List<String> numList = new ArrayList<String>();
    numList.add("1");
    numList.add("2");
    numList.add("3");
    numList.add("4");
        
    //This thread will iterate the list
    Thread thread1 = new Thread(){ 
      public void run(){ 
        try{ 
          Iterator<String> i = numList.iterator(); 
          while (i.hasNext()){ 
            System.out.println(i.next()); 
            // Using sleep to simulate concurrency
            Thread.sleep(1000); 
          }     
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread1 : Concurrent modification detected 
            on this list"); 
          e.printStackTrace();
        }catch(InterruptedException e){
          
        } 
      } 
    }; 
    thread1.start(); 
        
    // This thread will try to add to the collection,
    // while the collection is iterated by another thread.
    Thread thread2 = new Thread(){ 
      public void run(){ 
        try{ 
          // Using sleep to simulate concurrency
          Thread.sleep(2000);
          // adding new value to the shared list
          numList.add("5"); 
          System.out.println("new value added to the list"); 
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread2 : Concurrent modification detected 
                  on the List"); 
        } catch(InterruptedException e){}
      } 
    }; 
    thread2.start(); 
  }
}

Output

1
2
new value added to the list
thread1 : Concurrent modification detected on this list
java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
 at java.util.ArrayList$Itr.next(Unknown Source)
 at org.netjs.prog.FailFastDemo$1.run(FailFastDemo.java:24)

Here it can be seen that the ConcurrentModificationException is thrown because the list is changed by a thread while it has been iterated by another thread.

Now in the same code change the ArrayList to CopyOnWriteArrayList. Also added one sysout after adding new element to the list.

public class FailFastDemo {
  public static void main(String[] args) {
    List<String> numList = new CopyOnWriteArrayList<String>();
    numList.add("1");
    numList.add("2");
    numList.add("3");
    numList.add("4");
        
    //This thread will iterate the list
    Thread thread1 = new Thread(){ 
      public void run(){ 
        try{ 
          Iterator<String> i = numList.iterator(); 
          while (i.hasNext()){ 
            System.out.println(i.next()); 
            // Using sleep to simulate concurrency
            Thread.sleep(1000); 
          }     
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread1 : Concurrent modification detected 
            on this list"); 
          e.printStackTrace();
        }catch(InterruptedException e){
                    
        } 
      } 
    }; 
    thread1.start(); 
        
    // This thread will try to add to the collection,
    // while the collection is iterated by another thread.
    Thread thread2 = new Thread(){ 
      public void run(){ 
        try{ 
          // Using sleep to simulate concurrency
          Thread.sleep(2000);
          // adding new value to the shared list
          numList.add("5"); 
          System.out.println("new value added to the list"); 
          System.out.println("List " + numList);
        }catch(ConcurrentModificationException e){ 
          System.out.println("thread2 : Concurrent modification detected 
           on the List"); 
        } catch(InterruptedException e){}
      } 
    }; 
    thread2.start();    
  }
}

Output

1
2
3
new value added to the list
List [1, 2, 3, 4, 5]
4

Here ConcurrentModificationException is not thrown as CopyOnWriteArrayList is used now. Also note that, though one of the thread adds a new element and at that time the list prints all the elements from 1-5. But the iterator has the reference to the old copy of the list and it prints from 1-4.

Points to note

  • CopyOnWriteArrayList in Java provides a thread-safe alternative to the normal ArrayList.
  • In CopyOnWriteArrayList thread safety is achieved in a different way from a thread safe collection like Vector. In CopyOnWriteArrayList fresh copy of the underlying array is created with every mutative operations (add, set, and so on).
  • Because of this approach CopyOnWriteArrayList gives better performance in case there are more threads iterating the list than mutating it. Several threads can iterate CopyOnWriteArrayList concurrently.
  • CopyOnWriteArrayList's iterator is fail-safe and guaranteed not to throw ConcurrentModificationException.
  • CopyOnWriteArrayList's iterator uses a reference to the state of the array at the point that the iterator was created.
  • The iterator will not reflect additions, removals, or changes to the list since the iterator was created thus it is also known as "snapshot style" iterator.
  • All elements are permitted, including null.

That's all for this topic CopyOnWriteArrayList in Java With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. ConcurrentHashMap in Java With Examples
  2. Java CyclicBarrier With Examples
  3. Difference Between ArrayList And CopyOnWriteArrayList in Java
  4. Fail-Fast Vs Fail-Safe Iterator in Java
  5. Java Concurrency Interview Questions And Answers

You may also like-

  1. How ArrayList Works Internally in Java
  2. Java Collections Interview Questions And Answers
  3. How to Iterate a HashMap of ArrayLists of String in Java
  4. Java Program to Convert a File to Byte Array
  5. static reference to the non-static method or field error
  6. Race Condition in Java Multi-Threading
  7. Java ThreadLocal Class With Examples
  8. Interface Default Methods in Java

Tool Calling in LangChain

In the previous article Tools in LangChain With Examples we saw how you can create tools in LangChain and how to invoke those tools using invoke() method of the tool as every tool is a Runnable in LangChain which means it automatically inherits the .invoke() method from the Runnable interface. That works great when you are the one calling the tool. But the real magic happens when you let the LLM itself decide which tool to use, that’s where tool calling in LangChain comes into picture.

LangChain’s tool calling

Tools aren’t just standalone functions which you will execute manually, they become extensions of the LLM’s reasoning. You bind tools with the model so it can choose the appropriate tool call to get the result.

Integrating tools with the LLM

In LangChain you can use bind_tools() method to integrate your custom tools with the LLM. With bind_tools() method you can pass the list of tools to bind to the model.

By passing a list of tools to bind_tools(), you expose their name, description, and input schema to the model. This allows the LLM to recognize when a user query matches a tool’s purpose and respond with a structured ToolMessage, essentially a request to execute that tool.

It’s important to understand that the LLM does not read or execute your code directly. When it recognizes that a user request matches a tool’s purpose, it sends back a ToolMessage like:

{"name": "multiply", "args": {"a": 7, "b": 8}}

This is a signal that the tool should be run, but the execution itself is your responsibility.

Developer’s Role:

After receiving the tool call, following are the activities that needs to be done manually:

  1. Parsing the ToolMessage from the LLM’s response.
  2. Executing the tool with the provided arguments.
  3. Returning the result back to the model so it can continue reasoning and produce a polished answer.

If you want the orchestration to happen automatically, you’ll need to use an agent. Agents handle the loop of:

  1. Detecting tool calls,
  2. Running the tools,
  3. Feeding results back into the LLM,
  4. Continuing the conversation seamlessly.

In this article we’ll stick to manual bind_tools() workflow where the developer explicitly handles tool execution after the LLM emits a tool call. This approach helps you understand what’s happening under the hood before moving on to full agent automation.

Let’s try to make bind_tools() workflow clear with an example, we’ll use a multiply tool that multiplies two numbers.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_groq import ChatGroq
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file

# Multiplication tool
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

llm_with_tools = llm.bind_tools([multiply])

# Now the LLM can decide when to call the tool
response = llm_with_tools.invoke("What is 3 times 7?")
print(response)

Full Output

content='' additional_kwargs={'tool_calls': [{'id': 'kj0h3cwtm', 'function': {'arguments': '{"a":3,"b":7}', 'name': 'multiply'}, 
'type': 'function'}]} response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 228, 'total_tokens': 247,
'completion_time': 0.058486455, 'completion_tokens_details': None, 'prompt_time': 0.034593443, 'prompt_tokens_details': None,
'queue_time': 0.159857826, 'total_time': 0.093079898}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_45180df409', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df212-a97c-7ab3-b5be-a48bdeb26b31-0' 
tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 7}, 'id': 'kj0h3cwtm', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 228, 'output_tokens': 19, 'total_tokens': 247}

If you inspect the output you will see a tool call, with name and args, is returned by the model.

tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 7}, 'id': 'kj0h3cwtm', 'type': 'tool_call'}]

But LangChain won’t automatically execute the tool for you, as LLM doesn’t actually read your code. You, as a developer need to parse this tools call and invoke the required tool.

Given below is the complete code.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_groq import ChatGroq
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file

# Multiplication tool
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

llm_with_tools = llm.bind_tools([multiply])

# Now the LLM can decide when to call the tool
response = llm_with_tools.invoke("What is 3 times 7?")
print(response)

# if tool call is requested, it will be in response.tool_calls
if response.tool_calls:
    # loop through tool calls and execute them
    for tc in response.tool_calls:
        if tc["name"] == "multiply":
            result = multiply.invoke(tc["args"])
            print("Tool result:", result)

Output

Tool result: 21

Tool Calling with multiple tools

Here is another example, where we’ll see the workflow with multiple tool calls. Let’s say we have two different tools for addition and multiplication and tool call may contain both tools or any one of them. Workflow is as given below.

  1. LLM receives the user’s query.
  2. It decides whether the query needs external help (tool calling).
  3. If yes, appropriate ToolMessage is returned (tool call with name and args).
  4. The tool executes and returns a Result (e.g., live data, calculation, API response).
  5. That result is sent back to the LLM, which integrates it with the conversation.
  6. Finally, the LLM produces a polished, conversational answer for the user.

If you want to use a simple loop as shown in the previous example, then the code would be as given below.

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_groq import ChatGroq
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file

# Multiplication tool
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

# Addition tool
@tool
def add(a: int, b: int) -> int:
    """Add two integers and return the result."""
    return a + b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

# Bind tools
llm_with_tools = llm.bind_tools([multiply, add])

# System prompt template
system_prompt = """
You are an AI assistant with access to external tools.
Your job is to decide when to call a tool, execute it correctly,
and then provide a polished, conversational answer to the user.

Available tools:
- multiply(a: int, b: int): returns the product of two integers
- add(a: int, b: int): returns the sum of two integers

Guidelines:
1. Think step by step about whether a tool is needed.
2. When calling a tool, output a structured tool call in JSON.
3. If no tool is needed, answer directly
4. Do not expose internal reasoning or chain-of-thought.
5. Only output either a tool call or a final user-friendly answer.
"""

# Build prompt template
prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    ("user", "{input}")
])

def multi_tool_feedback(query: str):
    # First LLM call: decide tool usage
    first_response = llm_with_tools.invoke(query)
    print("First response:", first_response)
    results = []
    if first_response.tool_calls:
        print("Tool calls:", first_response.tool_calls)
        for tool_call in first_response.tool_calls:
            tool_name = tool_call["name"]
            args = tool_call["args"]

            # Execute each tool
            if tool_name == "multiply":
                result = multiply.invoke(args)
            elif tool_name == "add":
                result = add.invoke(args)
            else:
                result = "Unknown tool"

            results.append(result)

        print("Tool results:", results)
        # Second LLM call: feed all results back
        final_answer = llm.invoke(
            f"User asked: {query}. The tools returned: {results}. "
            "Please combine these into a natural language answer."
        )
        return final_answer
    else:
        # No tool calls, just return the model’s text
        return first_response

response = multi_tool_feedback("Please multiply 5 and 3, then add 10 to the result.")
print("Final response:", response)  
print("Final Answer:", response.content)

Output

First response: content='' additional_kwargs={'tool_calls': [{'id': '1e8aj5waj', 'function': {'arguments': '{"a":5,"b":3}', 'name': 'multiply'}, 'type': 'function'}, 
{'id': 'dbpynva42', 'function': {'arguments': '{"a":15,"b":10}', 'name': 'add'}, 'type': 'function'}]} 
response_metadata={'token_usage': {'completion_tokens': 36, 'prompt_tokens': 295, 'total_tokens': 331, 'completion_time': 0.076297873, 'completion_tokens_details': None, 'prompt_time': 0.014930285, 'prompt_tokens_details': None, 'queue_time': 0.157809024, 'total_time': 0.091228158}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_3272ea2d91', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df1cb-134e-7232-b741-b9e941c64389-0'
tool_calls=[{'name': 'multiply', 'args': {'a': 5, 'b': 3}, 'id': '1e8aj5waj', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 15, 'b': 10}, 'id': 'dbpynva42', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 295, 'output_tokens': 36, 'total_tokens': 331}
Tool calls: [{'name': 'multiply', 'args': {'a': 5, 'b': 3}, 'id': '1e8aj5waj', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 15, 'b': 10}, 'id': 'dbpynva42', 'type': 'tool_call'}]
Tool results: [15, 25]
Final response: content='To solve the problem, we first multiply 5 and 3, which equals 15. Then, we add 10 to the result, giving us a final answer of 25.' additional_kwargs={} response_metadata={'token_usage': {'completion_tokens': 39, 'prompt_tokens': 73, 'total_tokens': 112, 'completion_time': 0.124071334, 'completion_tokens_details': None, 'prompt_time': 0.006693922, 'prompt_tokens_details': None, 'queue_time': 0.063180878, 'total_time': 0.130765256}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_dae98b5ecb', 'service_tier': 'on_demand', 'finish_reason': 'stop', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df1cb-1507-7a33-aa60-6a5bd288b607-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 73, 'output_tokens': 39, 'total_tokens': 112}
Final Answer: To solve the problem, we first multiply 5 and 3, which equals 15. Then, we add 10 to the result, giving us a final answer of 25.

Points to Note

  • The system message is made more descriptive to give clarity to the LLM about the tools and their arguments.
  • While executing the tools, their results are appended to a list.
  • That list and the original query is sent again to the LLM to get a final, polished answer.

Multi- tool calling with chain.

Same example can be done in a more professional way by creating a chain and using RunnableLambda to execute functions.

from langchain_groq import ChatGroq
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain_core.runnables import RunnableLambda
from dotenv import load_dotenv

load_dotenv()  # Load environment variables from .env file
# Define tools
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers and return the result."""
    return a * b

@tool
def add(a: int, b: int) -> int:
    """Add two integers and return the result."""
    return a + b

# Initialize model
llm = ChatGroq(model="llama-3.3-70b-versatile", temperature=0.5)

# Bind tools
llm_with_tools = llm.bind_tools([multiply, add])

# 4. System prompt template
system_prompt = """
You are an AI assistant with access to external tools.
Your job is to decide when to call a tool, execute it correctly,
and then provide a polished, conversational answer to the user.

Available tools:
- multiply(a: int, b: int): returns the product of two integers
- add(a: int, b: int): returns the sum of two integers

Guidelines:
1. Think step by step about whether a tool is needed.
2. When calling a tool, output a structured tool call in JSON.
3. If no tool is needed, answer directly
4. Do not expose internal reasoning or chain-of-thought.
5. Only output either a tool call or a final user-friendly answer.
"""

# Build prompt template
prompt = ChatPromptTemplate.from_messages([
    ("system", system_prompt),
    ("user", "{input}")
])

# Define tool executor
def execute_tools(output):
    print("LLM output after first call:", output)
    results = []
    if output.tool_calls:
        for tc in output.tool_calls:
            if tc["name"] == "add":
                results.append(add.invoke(tc["args"]))
            elif tc["name"] == "multiply":
                results.append(multiply.invoke(tc["args"]))
            
    return {"tool_results": results, "query": output.content}

# Feed results back into LLM
def final_answer(data):
    return llm.invoke(
        f"User asked: {data['query']}. Tools returned: {data['tool_results']}. "
        "Please combine these into a natural language answer."
    )

# Chain
chain = (prompt 
        | llm_with_tools 
        | RunnableLambda(execute_tools) 
        | RunnableLambda(final_answer)
)

response = chain.invoke({"input": "Please add 5 and 3, then multiply the result by 2."})
print("Final response:", response)  
print("Final Answer:", response.content)

Output

LLM output after first call: content='' additional_kwargs={'tool_calls': [{'id': 'vz71y3drr', 'function': {'arguments': '{"a":5,"b":3}', 'name': 'add'}, 'type': 'function'}, {'id': '14hvbadej', 'function': {'arguments': '{"a":8,"b":2}', 'name': 'multiply'}, 'type': 'function'}]} 
response_metadata={'token_usage': {'completion_tokens': 36, 'prompt_tokens': 441, 'total_tokens': 477, 'completion_time': 0.092313015, 'completion_tokens_details': None, 'prompt_time': 0.022607, 'prompt_tokens_details': None, 'queue_time': 0.04985095, 'total_time': 0.114920015}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_dae98b5ecb', 'service_tier': 'on_demand', 'finish_reason': 'tool_calls', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df260-48af-7080-9360-37fc5caad39c-0'
tool_calls=[{'name': 'add', 'args': {'a': 5, 'b': 3}, 'id': 'vz71y3drr', 'type': 'tool_call'}, {'name': 'multiply', 'args': {'a': 8, 'b': 2}, 'id': '14hvbadej', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 441, 'output_tokens': 36, 'total_tokens': 477}
Final response: content='The tools returned two values: 8 and 16.' additional_kwargs={} response_metadata={'token_usage': {'completion_tokens': 13, 'prompt_tokens': 57, 'total_tokens': 70, 'completion_time': 0.034086635, 'completion_tokens_details': None, 'prompt_time': 0.003220668, 'prompt_tokens_details': None, 'queue_time': 0.161129671, 'total_time': 0.037307303}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_3272ea2d91', 'service_tier': 'on_demand', 'finish_reason': 'stop', 'logprobs': None, 'model_provider': 'groq'} id='lc_run--019df260-4b3a-73d2-b4ce-79a0fae5297a-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 57, 'output_tokens': 13, 'total_tokens': 70}
Final Answer: The tools returned two values: 8 and 16.

InjectedToolArg in Langchain tools

When building tools in LangChain, not every argument should come from the LLM. Some values, like session IDs, user tokens, timestamps, or database handles, are sensitive or system specific and must be injected at runtime. That’s exactly what InjectedToolArg in LangChain is designed for.

InjectedToolArg is a LangChain Core annotation that marks tool arguments as injected at runtime rather than generated by the LLM.

Arguments marked with InjectedToolArg are excluded from the schema sent to the model. The LLM never sees or generates them. That prevents the LLM from guessing or hallucinating values that should come from the environment (e.g., conversation IDs, request metadata, API keys).

Arguments marked with InjectedToolArg are provided by the developer, agent executor, or system context during tool execution.

InjectedToolArg Langchain example

Here is a simple example where Customer ID argument is marked as a InjectedToolArg, which means LLM won’t see it and even won’t try to guess it on its own. It has to be injected at runtime.

from typing import Annotated
from langchain_core.tools import tool, InjectedToolArg
from langchain_google_genai import ChatGoogleGenerativeAI
from dotenv import load_dotenv

load_dotenv()

# Define a tool with one normal arg (LLM-supplied) and one 
# injected arg (runtime-supplied)
@tool
def escalate_to_human(
    reason: str,
    customer_id: Annotated[int, InjectedToolArg]
) -> str:
    """Escalate the conversation to a human operator."""
    return f"Escalating: {reason} (Customer ID: {customer_id})"

# Create the LLM
llm = ChatGoogleGenerativeAI(model="gemini-3.1-flash-lite-preview", temperature=0.2)

# Bind the tool to the LLM
llm_with_tools = llm.bind_tools([escalate_to_human])

query = "The customer is very upset, escalate this."

# LLM processes the query and emits a tool call
response = llm_with_tools.invoke(query)

print("LLM raw response:", response)

if response.tool_calls:
    for tc in response.tool_calls:
        # Inject runtime context (customer_id from your system/session)
        # hardcodinhg customer_id=12345 for demo purposes
        runtime_context = {"customer_id": 12345}
        merged_args = {**tc["args"], **runtime_context}
        result = escalate_to_human.invoke(merged_args)
        print("Tool execution result:", result)
        # Feed the tool result back into the LLM to get final response
        response = llm.invoke(
            f"""
            Greet the customer using Customer ID (get from context) and inquire about the issue. 
            Context: {result}
            """
        )

print("Final LLM Response:", response)

print("Final LLM answer:", response.content[0].get("text"))

Output

LLM raw response: content=[] additional_kwargs={'function_call': {'name': 'escalate_to_human', 'arguments': '{"reason": "The customer is very upset and has requested an escalation."}'}, '__gemini_function_call_thought_signatures__': {'312283e6-b7ba-4149-97dd-1c93e5e97d8a': 'EjQKMgEMOdbHi4YjgRE760fr4+mtE0xB7dY5NZ3b3Dw6aZy70QE9eyXU9eDGgGKvMN8yu5z2'}} response_metadata={'finish_reason': 'STOP', 'model_name': 'gemini-3.1-flash-lite-preview', 'safety_ratings': [], 'model_provider': 'google_genai'} id='lc_run--019df2d2-81f9-75f3-9a97-f44e2ab3e660-0' 
tool_calls=[{'name': 'escalate_to_human', 'args': {'reason': 'The customer is very upset and has requested an escalation.'}, 'id': '312283e6-b7ba-4149-97dd-1c93e5e97d8a', 'type': 'tool_call'}] invalid_tool_calls=[] usage_metadata={'input_tokens': 59, 'output_tokens': 29, 'total_tokens': 88, 'input_token_details': {'cache_read': 0}}
Tool execution result: Escalating: The customer is very upset and has requested an escalation. (Customer ID: 12345)
Final LLM Response: content=[{'type': 'text', 'text': 'Hello, Customer 12345. I understand that you are very upset and have requested an escalation. I am here to assist you—could you please tell me more about the issue you are experiencing so I can address your concerns immediately?', 'extras': {'signature': 'EjQKMgEMOdbHAXp2AIc7nJffkQs5eevUxSKQmBMqsO/cAn089Zu+2eOdbvLIF6uunch0lbwA'}}] additional_kwargs={} response_metadata={'finish_reason': 'STOP', 'model_name': 'gemini-3.1-flash-lite-preview', 'safety_ratings': [], 'model_provider': 'google_genai'} id='lc_run--019df2d2-870d-71d3-9b4b-56ad10346d96-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 52, 'output_tokens': 50, 'total_tokens': 102, 'input_token_details': {'cache_read': 0}}
Final LLM answer: Hello, Customer 12345. I understand that you are very upset and have requested an escalation. I am here to assist you—could you please tell me more about the issue you are experiencing so I can address your concerns immediately?

That’s all for this topic Tool Calling in LangChain. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Citation Aware RAG Application in LangChain
  2. LangChain Conversational RAG with Multi-user Sessions
  3. RunnableBranch in LangChain With Examples
  4. Structured Output In LangChain
  5. Messages in LangChain

You may also like-

  1. Chatbot With Chat History - LangChain MessagesPlaceHolder
  2. Document Loaders in LangChain With Examples
  3. Array in Java With Examples
  4. Java CyclicBarrier With Examples
  5. Python assert Statement
  6. Multiple Inheritance in Python
  7. Spring Boot REST API Documentation - OpenAPI, Swagger
  8. Bean Scopes in Spring With Examples

Saturday, May 2, 2026

Equality And Relational Operators in Java

In Java, the equality and relational operators are used to compare values and determine the relationship between operands. These operators determine whether one value is greater than, less than, equal to, or not equal to another.

The equality and relational operators generate a boolean result; true if the condition holds, and false if it does not. This makes Equality And Relational Operators in Java essential for decision‑making and control flow in programs.

List of Equality and Relational operators

Operator Description
==equal to
!=not equal to
>greater than
>=greater than or equal to
<less than
<=less than or equal to

Important Note- You must use "==" when testing two primitive values for equality. Do not confuse it with the assignment operator "=", which assigns a value to a variable.

Example:
int a = 5, b = 5;
System.out.println(a == b); // true
System.out.println(a = b);  // assigns b to a, doesn't compare

Refer post Difference between equals() method and equality operator == in Java to know when to use equals method and when to use == operator.

Java equality and relational operators example

Following example shows the Java equality and relational operators in action.

public class RelationalDemo {
  public static void main(String[] args) {
    int a = 7;
    int b = 5;
    int c = 7;
    
    // This should get printed
    if(a == c){
      System.out.println("Values of a and c are equal");
    }
        
    // This won't be printed
    if(a == b){
      System.out.println("Values of a and b are equal");
    }
    
    if(a != b){
      System.out.println("Values of and b are not equal");
    }
    
    if(a > b){
      System.out.println("a is greater than b");
    }
        
    if(a >= c){
      System.out.println("a is greater than or equal to c");
    }
    
    // This won't be printed
    if(a < b){
      System.out.println("a is less than b");
    }
    
    if(b < a){
      System.out.println("b is less than a");
    }
  }
}

Output

Values of a and c are equal
Values of and b are not equal
a is greater than b
a is greater than or equal to c
b is less than a

That's all for this topic Equality And Relational Operators in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Basics Tutorial Page


Related Topics

  1. Arithmetic And Unary Operators in Java
  2. Conditional Operators in Java
  3. Array in Java
  4. String in Java Tutorial
  5. this Keyword in Java With Examples

You may also like-

  1. What are JVM, JRE and JDK in Java
  2. Java Automatic Numeric Type Promotion
  3. Why main Method static in Java
  4. Java Pass by Value or Pass by Reference
  5. Access Modifiers in Java - Public, Private, Protected and Default
  6. ArrayList in Java With Examples
  7. ConcurrentHashMap in Java With Examples
  8. Lambda Expressions in Java 8

throw Statement in Java Exception Handling

It is possible for a Java program to explicitly raise an exception, that is done using the throw statement in Java.

When the throw statement is executed inside a method, the program’s flow of execution halts immediately; any code written after the throw statement will not run.

General Syntax of throw Statement

General form of throw statement is-

throw throwableObject;
Here, throwableObject must be an instance of Throwable class or one of its subclasses.

Ways to Obtain a Throwable Object

We can get this throwableObject in 2 ways-

  • By using the Exception parameter in a catch block- re‑throwing a caught exception.
  • Create a new exception object using the new operator. For example
    throw new IllegalArgumentException("Invalid input provided");
    

Example Program Using throw Statement in Java

public class ThrowDemo {

 public static void main(String[] args) {
  ThrowDemo throwDemo = new ThrowDemo();
  try{
    throwDemo.displayValue();
  }catch(NullPointerException nExp){
     System.out.println("Exception caught in catch block of main");
     nExp.printStackTrace();;
  }
 }
 
 public void displayValue(){
  try{
    // Explicitly throwing a new exception object
    throw new NullPointerException();   
  }catch(NullPointerException nExp){
     System.out.println("Exception caught in catch block of displayValue");
     // Re‑throwing the caught exception
     throw nExp;
  }
 }
}

Note that in this program throw keyword is used at two places. First time, in the try block of displayValue() method, it uses the new operator to create an instance of type throwable.

Second time it uses the parameter of the catch block.

throw statement helps in preserving loose coupling

One of the best practices for the exception handling is to preserve loose coupling between layers of an application. This means that an implementation specific checked exception should not propagate to another layer.

As Example SQL exception from the DataAccessCode (DAO layer) should not propagate to the service (Business) layer. The common approach in that case is to wrap or convert database specific SQLException into an unchecked exception and then throw it.

catch(SQLException ex){
    throw new RuntimeException("Database error occurred", ex);
}

This ensures that the service layer deals with generalized exceptions rather than being tightly coupled to database‑specific details, making the application more maintainable and flexible.

That's all for this topic throw Statement in Java Exception Handling. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Difference Between throw And throws in Java
  2. Exception Propagation in Java Exception Handling
  3. Try-With-Resources in Java With Examples
  4. Creating Custom Exception Class in Java
  5. Java Exception Handling Interview Questions And Answers

You may also like-

  1. Fail-Fast Vs Fail-Safe Iterator in Java
  2. How HashMap Works Internally in Java
  3. finalize() Method in Java
  4. Java Pass by Value or Pass by Reference
  5. Why Class Name And File Name Should be Same in Java
  6. Java CyclicBarrier With Examples
  7. Lambda Expressions in Java 8
  8. Count Number of Times Each Character Appears in a String Java Program

Transaction Management in Java-JDBC

Efficient Transaction Management in Java is critical for building reliable, enterprise‑grade applications. JDBC provides powerful APIs to start, commit, roll back, and even set savepoints within transactions, ensuring your database always remains consistent.


Tools in LangChain With Examples

In this post we'll explore the tools in LangChain.

Large Language Models (LLMs) are powerful at generating text, reasoning, and summarizing but they have inherent limitations:

  • No real-time web search- They cannot fetch fresh information beyond their training data. Though many of the LLMs now have web search capability integrated like GPT leverages Bing search, Gemini uses Google search.
  • No API calling ability- LLMs operate in isolation, they cannot directly interact with external systems or services. That limitation becomes a roadblock if you want your LLM responses to be integrated with your APIs or some third-party platforms.
  • No structured computation- LLMs aren’t built for precise math or database-style operations. They can approximate answers, but when it comes to exact calculations, SQL queries, or deterministic logic, they often fall short.
  • No direct integration with workflows- They cannot trigger external actions like sending emails or querying CRMs.

These gaps make LLMs less useful in production environments where up to date data, structured outputs, and external integrations are critical. This is exactly where LangChain’s concept of tools comes in.

Tools in LangChain

Tools act as bridges between the LLM and the outside world, enabling it to fetch live information, perform structured computations, or call APIs seamlessly. In other words, you can say that, tools transform an isolated LLM into a connected, production‑ready system.

Behind the scenes, tools are just functions with well‑defined inputs and outputs. The chat model decides when to call them and what arguments to pass, based on the flow of the conversation. This decision process, known as tool calling, happens when the model detects that a user's request can't be answered with text alone and instead requires an external function, like fetching live data or performing a calculation.

Types of tools in LangChain

LangChain primarily categorizes tools into-

  1. Built-in Tools- LangChain itself provides lots of pre-configured tools covering many scenarios across search, API calling, DB tasks. Refer https://docs.langchain.com/oss/python/integrations/tools for available built-in tools in LangChain.
    • RequestsTool for API calls (RequestsGetTool, RequestsPostTool etc.)
    • WikipediaQueryRun to connect to Wikipedia
    • DuckDuckGo Search, Google Serper, Google Search etc. for online searches
    • Python REPL Tool to execute arbitrary Python code within a shell environment, primarily used for complex calculations, data analysis and many more such built-in tools.
  2. Custom tools- You can create your own custom tool to encapsulate specific business logic. You can create custom tools in LangChain using the @tool decorator or by subclassing BaseTool.
  3. Toolkits: You can bundle related tools to accomplish specific tasks, such as the Gmail toolkit, GitHub toolkit, or SQL Database toolkit. For example, SQL Database toolkit contains the following tools-
    • QuerySQLDatabaseTool- Which takes SQL query as input, output is a result from the database.
    • InfoSQLDatabaseTool- Input is comma-separated list of tables, output is the schema and sample rows for those tables.
    • ListSQLDatabaseTool- This tool is used to retrieve a comma-separated list of table names within a SQL database
    • QuerySQLCheckerTool- This tool is used to double check if passed query is correct or not before executing it

Built-in Tools Examples in LangChain

Let’s see some examples of integrations with built-in tools.

  1. Using DuckDuckGo search
    from langchain_community.tools import DuckDuckGoSearchRun
    
    search = DuckDuckGoSearchRun()
    
    result = search.invoke("List 5 main sports events of 2026.")
    
    print(result)
    
  2. Using RequestsGetTool to call an API get method. Jsonplaceholder free online REST API is used here.
    from langchain_community.tools import RequestsGetTool
    from langchain_community.utilities.requests import JsonRequestsWrapper
    # Initialize the wrapper for handling responses
    requests_wrapper = JsonRequestsWrapper()
    
    # allow_dangerous_requests=True is mandatory for security compliance
    # wrapper is also mandatory otherwise pydantic validator will fail since the tool expects a wrapper to be passed in
    requests_tool = RequestsGetTool(requests_wrapper=requests_wrapper, allow_dangerous_requests=True)  # Enable dangerous requests for demonstration
    
    result = requests_tool.invoke("https://jsonplaceholder.typicode.com/posts?_limit=2")
    print(result)
    

    Output

    [{'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'}, {'userId': 1, 'id': 2, 'title': 'qui est esse', 'body': 'est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla'}]
    
  3. Using Wikipedia tool You must install the wikipedia Python package to use this integration.
    from langchain_community.tools import WikipediaQueryRun
    from langchain_community.utilities import WikipediaAPIWrapper
    # Initialize the wrapper and tool
    api_wrapper = WikipediaAPIWrapper(top_k_results=3, doc_content_chars_max=400)
    wikipedia = WikipediaQueryRun(api_wrapper=api_wrapper)
    
    result = wikipedia.invoke("Indian Economy")
    print(result)
    

Custom Tools Example in LangChain

The simplest way to create a tool is with the @tool decorator. Let’s create a simple function to multiple two numbers as a tool in LangChain.

from langchain.tools import tool

@tool
def multiply(a: float, b: float) -> float:
    """Multiply two numbers."""
    return a * b

result = multiply.invoke({"a": 3, "b": 4})
print(result)

Output

12.0

Points to note here-

  1. A function becomes a tool in LangChain by decorating it with @tool, which exposes it with a clear interface for the model to call.
  2. In LangChain, tools are Runnable objects by default. This means they can be executed with the .invoke() method, where you pass inputs as a dictionary that matches the tool’s defined schema.
  3. Type hints in the function are essential because they define the tool's input schema, ensuring the model knows exactly what arguments to expect. The docstring should be short yet descriptive, giving the model enough context to understand the tool’s purpose and when to use it.
  4. By default, the tool name comes from the function name. You can customize it by passing a name with @tool decorator, for example @tool("multiply_tool")

Metadata about tools

In LangChain, once you’ve defined a tool, you can inspect its arguments and description using built-in methods and attributes.

The most useful ones are:

  • tool.args- Returns the input schema (usually a dictionary or Pydantic model) that defines the tool’s expected arguments.
  • tool.description- Returns the docstring you provided, which explains the tool’s purpose.
  • tool.name- Gives the tool’s registered name (helpful when binding multiple tools).
  • tool.input_schema- Shows the full schema object for structured validation.
  • tool.__doc__- Standard Python docstring access, same as description.

Example showing tool metadata:

from langchain.tools import tool

@tool
def multiply(a: float, b: float) -> float:
    """Multiply two numbers."""
    return a * b

result = multiply.invoke({"a": 3, "b": 4})
print(result)

print("tool name:", multiply.name)         
print("tool description:", multiply.description)  
print("tool args:", multiply.args)         
print("tool input schema:", multiply.input_schema) 
print("tool docstring:", multiply.__doc__) 

Output:

12.0
tool name: multiply
tool description: Multiply two numbers.
tool args: {'a': {'title': 'A', 'type': 'number'}, 'b': {'title': 'B', 'type': 'number'}}
tool input schema: <class 'langchain_core.utils.pydantic.multiply'>
tool docstring: Tool that can operate on any number of inputs.

How LLM sees a tool

When you decorate a function with @tool, LangChain wraps it into a Runnable object and exposes metadata that the LLM can read.

  • Tool name- The identifier it can call (e.g., "multiply").
  • Description- Taken from the docstring, which tells the model what the tool does and when it should be used.
  • Arguments schema- Derived from type hints (or a Pydantic model), so the model knows what inputs are required and their types.

The LLM doesn’t actually read your Python code. Instead, it sees the tool’s name, description, and input schema. When it recognizes that a user request needs a tool call, it wraps tool’s name, description, and input schema and sends that information back as a ToolMessage, indicating that this tool needs to be executed to get the required information. This flow is covered in more detail in Tool Calling in LangChain.

Using Pydantic schema in tools

You can define complex input schema with Pydantic models or JSON schemas. Using args_schema parameter you can specify the input schema for the tool.

It tells the LLM exactly what arguments the tool expects, their types, and their descriptions. Benefits you get are-

  • Ensures that arguments passed to the tool match the expected types and constraints.
  • Uses Pydantic models to enforce strict validation (e.g., int, str, Literal).

If you want the input to a tool to strictly follow a schema, for example, a Person model with fields name, address, address_type (restricted to "home", "office", or "other"), and age. You can define this schema using a Pydantic model. That model is then passed to the tool via the args_schema parameter, ensuring that all inputs are validated against the defined structure before the tool executes.

from langchain.tools import tool
from typing import Literal
from pydantic import BaseModel, Field

class Person(BaseModel):
    name: str  = Field(description="The name of the person")
    address: str = Field(description="The address of the person")
    address_type: Literal["home", "office", "other"] = Field(description="The type of address 'home', 'office', or 'other'")
    age: int = Field(description="The age of the person")

@tool(args_schema=Person)
def person_info_tool(name: str, address: str, address_type: str, age: int) -> str:
    """Tool to get information about a person."""
    person = Person(
        name=name,
        address=address,
        address_type=address_type,
        age=age
    )
    return person.model_dump_json()  # Return the person information as a JSON string

result = person_info_tool.invoke({
    "name": "Ram",
    "address": "123 VIP Street, New Delhi",
    "address_type": "Resident",
    "age": 30       
})     

print(result)

Output

{"name":"Ram","address":"123 VIP Street, New Delhi","address_type":"home","age":30}

Note that, when you define a tool with args_schema, the recommended style is:

  • Use a Pydantic model to describe the arguments (with Field for descriptions).
  • Unpack the fields directly in the function signature (instead of passing the whole model object).

Tool using Base Tool

Since all tools in LangChain inherit from the abstract class BaseTool, you can also create a custom tool by subclassing BaseTool directly and implementing its required methods.

To create a custom tool by subclassing BaseTool, you must define the following attributes and methods:

  • name: A unique string identifying the tool for the model.
  • description: A natural language explanation telling the model when and how to use the tool.
  • args_schema: (Optional) A Pydantic BaseModel that validates the tool's input and informs the agent about required arguments.
  • _run: The synchronous logic for the tool's execution.
  • _arun: (Optional) The asynchronous implementation of the tool; if not defined, it will raise a NotImplementedError

Example of a custom tool:

Taking the same example of a multiply tool, you can also implement it by extending the BaseTool class directly, as shown below:

from pydantic import BaseModel, Field
from langchain_core.tools import BaseTool
from typing import Type

#pydantic model to define the input schema for the tool
class MultiplyArgs(BaseModel):
    # ... means this field is required and has no default value.
    a: float = Field(..., description="The first number to multiply.")
    b: float = Field(..., description="The second number to multiply.")

class Multiply(BaseTool):
    name: str = "multiply"
    description: str = "Multiply two numbers together."
    args_schema: Type[BaseModel] = MultiplyArgs

    def _run(self, a: float, b: float) -> float:
        """Multiply two numbers."""
        return a * b
    def _arun(self, a: float, b: float) -> float:
        """Asynchronous version of the multiply tool."""
        raise NotImplementedError("This tool does not support async yet.")
    
multiply_tool = Multiply()
result = multiply_tool.invoke({"a": 2.5, "b": 6.7})
print(result)

Output:

16.75

What to use @tool or BaseTool

Both @tool and BaseTool are valid ways to create tools in LangChain, but they serve slightly different purposes.

@tool Decorator is best used for quick, simple tool creation.

  • Advantages:
    • Minimal boilerplate, just write a function.
    • Easy to add descriptions and schemas.
  • Limitations:
    • Lets less control over advanced behaviors (custom validation, async handling, logging).

BaseTool subclass is best used for complex, highly customized tools.

  • Advantages:
    • Full control over execution (sync/async, error handling, logging).
    • Useful when building reusable, production-grade tools.
  • Limitations:
    • More boilerplate code.
    • Slightly harder to maintain for simple use cases.

Toolkit in LangChain

In LangChain, a toolkit is a collection of related tools designed to be used together for specific tasks, such as interacting with a database, a JSON file, or an external API. One of the advantages of toolkit is reusability- Toolkits can be reused across different agents or workflows.

The steps for creating a custom toolkit in LangChain are as given below:

  1. Define Individual Tools
  2. Before creating a toolkit, you must define the individual tools it will contain.

  3. Create the Toolkit Class
    • Inherit from BaseToolkit: Create a custom class that inherits from langchain_core.tools.BaseToolkit.
    • Implement get_tools(): Every toolkit must implement a get_tools() method that returns a list of the tools it contains.

Creating custom toolkit – LangChain Example

from langchain.tools import tool
from langchain.tools import BaseTool
from langchain_core.tools import BaseToolkit
from typing import List

@tool()
def multiply(a: float, b: float) -> float:
    """Multiply two numbers."""
    return a * b

@tool()
def add(a: float, b: float) -> float:
    """Add two numbers."""
    return a + b

class MyToolKit(BaseToolkit):
    def get_tools(self) -> List[BaseTool]:
        return [multiply, add]

# create an instance of the toolkit and retrieve tools
toolkit = MyToolKit()
tools = toolkit.get_tools() 
for tool in tools:
    print(f"Tool name: {tool.name}")
    print(f"Tool description: {tool.description}")
    print(f"Tool args: {tool.args}")
    print(f"Tool input schema: {tool.input_schema}")
    print(f"Tool docstring: {tool.__doc__}")
    result = tool.invoke({"a": 5, "b": 3})
    print(f"Result of invoking {tool.name}: {result}\n")

Use Cases for LangChain Tools with Agents

The following are some use cases for LangChain tools with agents:

  1. Web Search Tool
    • Fetch the latest information such as stock prices, breaking news, or real time events.
  2. Math Tool
    • Perform precise calculations or numerical reasoning that go beyond the LLM’s built in capabilities.
  3. Database Query Tool
    • Retrive structured data like customer records, product inventories, or transaction histories from a connected database.
  4. Email Tool
    • Draft, format, and send professional emails or notifications directly from within the agent workflow.
  5. GitHub Tool
    • Interact with GitHub repositories- add or update files, fetch branch information, review pull requests, or automate project tasks.

That's all for this topic Tools in LangChain With Examples. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. LangChain Conversational RAG with Multi-user Sessions
  2. Citation Aware RAG Application in LangChain
  3. Embeddings in LangChain With Examples
  4. Vector Stores in LangChain With Examples
  5. RunnableBranch in LangChain With Examples

You may also like-

  1. Messages in LangChain
  2. Output Parsers in LangChain With Examples
  3. Unmodifiable or Immutable List in Java
  4. Write to a File in Java
  5. Interface in Python
  6. Tuple in Python With Examples
  7. React Virtual DOM
  8. Spring MVC Exception Handling - @ExceptionHandler And @ControllerAdvice Example