Snippets Collections
//One Thread method that a supervisor thread may use to monitor another thread is .isAlive(). This method returns true if the thread is still running, and false if it has terminated. A supervisor might continuously poll this value (check it at a fixed interval) until it changes, and then notify the user that the thread has changed state.

import java.time.Instant;
import java.time.Duration;
 
public class Factorial {
 public int compute(int n) {
   // the existing method to compute factorials
 }
 
 // utility method to create a supervisor thread
 public static Thread createSupervisor(Thread t) {
   Thread supervisor = new Thread(() -> {
     Instant startTime = Instant.now();
     // supervisor is polling for t's status
     while (t.isAlive()) {
       System.out.println(Thread.currentThread().getName() + " - Computation still running...");
       Thread.sleep(1000);
     }
   });
 
   // setting a custom name for the supervisor thread
   supervisor.setName("supervisor");
   return supervisor;
 
 }
 
 public static void main(String[] args) {
   Factorial f = new Factorial();
 
   Thread t1 = new Thread(() -> {
     System.out.println("25 factorial is...");
     System.out.println(f.compute(25));
   });
 
 
   Thread supervisor = createSupervisor(t1);
 
   t1.start();
   supervisor.start();
 
   System.out.println("Supervisor " + supervisor.getName() + " watching worker " + t1.getName());
 }
}
mport java.util.Random;

// Checkpoint 1
// public class CrystalBall implements Runnable {
  
public class CrystalBall{

  /* Instance Variables */
  // Removed in checkpoint 3
  /* Constructors */
  // Removed in checkpoint 3
  /* Instance Methods */
  // Removed in checkpoint 3

  public void ask(Question question) {
    System.out.println("Good question! You asked: " + question.getQuestion());
    this.think(question);
    System.out.println("Answer: " + this.answer());
  }

  private void think(Question question) {
    System.out.println("Hmm... Thinking");
    try {
      Thread.sleep(this.getSleepTimeInMs(question.getDifficulty()));
    } catch (Exception e) {
      System.out.println(e);
    }
    System.out.println("Done!");
  }

  private String answer() {
    String[] answers = {
        "Signs point to yes!",
        "Certainly!",
        "No opinion",
        "Answer is a little cloudy. Try again.",
        "Surely.",
        "No.",
        "Signs point to no.",
        "It could very well be!"
    };
    return answers[new Random().nextInt(answers.length)];
  }

  private int getSleepTimeInMs(Question.Difficulty difficulty) {
    switch (difficulty) {
      case EASY:
        return 1000;
      case MEDIUM:
        return 2000;
      case HARD:
        return 3000;
      default:
        return 500;
    }
  }
}

mport java.util.Random;

// Checkpoint 1
// public class CrystalBall implements Runnable {
  
public class CrystalBall{

  /* Instance Variables */
  // Removed in checkpoint 3

  /* Constructors */
  // Removed in checkpoint 3

  /* Instance Methods */
  // Removed in checkpoint 3

  public void ask(Question question) {
    System.out.println("Good question! You asked: " + question.getQuestion());
    this.think(question);
    System.out.println("Answer: " + this.answer());
  }

  private void think(Question question) {
    System.out.println("Hmm... Thinking");
    try {
      Thread.sleep(this.getSleepTimeInMs(question.getDifficulty()));
    } catch (Exception e) {
      System.out.println(e);
    }
    System.out.println("Done!");
  }

  private String answer() {
    String[] answers = {
        "Signs point to yes!",
        "Certainly!",
        "No opinion",
        "Answer is a little cloudy. Try again.",
        "Surely.",
        "No.",
        "Signs point to no.",
        "It could very well be!"
    };
    return answers[new Random().nextInt(answers.length)];
  }

  private int getSleepTimeInMs(Question.Difficulty difficulty) {
    switch (difficulty) {
      case EASY:
        return 1000;
      case MEDIUM:
        return 2000;
      case HARD:
        return 3000;
      default:
        return 500;
    }
  }
}
public class Factorial implements Runnable {
 private int n;
 
 public Factorial(int n) {
   this.n = n;
 }
 
 public int compute(int n) {
   // ... the code to compute factorials
 }
 
 public void run() {
   System.out.print("Factorial of " + String.valueOf(this.n) + ":")
   System.out.println(this.compute(this.n));
 }
 
 public static void main(String[] args) {
   Factorial f = new Factorial(25);
   Factorial g = new Factorial(10);
   Thread t1 = new Thread(f);
   Thread t2 = new Thread(f);
   t1.start();
   t2.start();
 }
}

//Another way of using the Runnable interface, which is even more succinct, is to use lambda expressions
public class Factorial {
 public int compute(int n) {
   // ... the code to compute factorials
 }
 
 public static void main(String[] args) {
   Factorial f = new Factorial();
 
   // the lambda function replacing the run method
   new Thread(() -> {
     System.out.println(f.compute(25));
   }).start();
 
   // the lambda function replacing the run method
   new Thread(() -> {
     System.out.println(f.compute(10));
   }).start();
 }
}
/*Extended the Thread Class
Created and Overrode a .run() method from Thread
Instantiated HugeProblemSolver and called .start() which signifies to start a new thread and search in the class for the .run() method to execute.*/
//CrystalBall
import java.util.Random;

public class CrystalBall extends Thread{
  private Question question;

  public CrystalBall(Question question){
    this.question = question;
    }

  @Override
  public void run() {
    ask(this.question);
  }


  private int getSleepTimeInMs(Question.Difficulty difficulty) {
    switch (difficulty) {
      case EASY:
        return 1000;
      case MEDIUM:
        return 2000;
      case HARD:
        return 3000;
      default:
        return 500;
    }
  }

  private String answer() {
    String[] answers = {
        "Signs point to yes!",
        "Certainly!",
        "No opinion",
        "Answer is a little cloudy. Try again.",
        "Surely.",
        "No.",
        "Signs point to no.",
        "It could very well be!"
    };
    return answers[new Random().nextInt(answers.length)];
  }

  private void think(Question question) {
    System.out.println("Hmm... Thinking");
    try {
      Thread.sleep(this.getSleepTimeInMs(question.getDifficulty()));
    } catch (Exception e) {
      System.out.println(e);
    }
    System.out.println("Done!");
  }

  public void ask(Question question) {
    System.out.println("Good question! You asked: " + question.getQuestion());
    this.think(question);
    System.out.println("Answer: " + this.answer());
  }
}

//FortuneTeller
import java.util.Arrays;
import java.util.List;

public class FortuneTeller {

  public static void main(String[] args) {

    List<Question> questions = Arrays.asList(
        new Question(Question.Difficulty.EASY, "Am I a good coder?"),
        new Question(Question.Difficulty.MEDIUM, "Will I be able to finish this course?"),
        new Question(Question.Difficulty.EASY, "Will it rain tomorrow?"),
        new Question(Question.Difficulty.EASY, "Will it snow today?"),
        new Question(Question.Difficulty.HARD, "Are you really all-knowing?"),
        new Question(Question.Difficulty.HARD, "Do I have any hidden talents?"),
        new Question(Question.Difficulty.HARD, "Will I live to be greater than 100 years old?"),
        new Question(Question.Difficulty.MEDIUM, "Will I be rich one day?"),
        new Question(Question.Difficulty.MEDIUM, "Should I clean my room?")
    );

    questions.stream().forEach(q -> {
      CrystalBall c = new CrystalBall(q);
      c.start();
    });
  }
}
//Shadowing allows for the overlapping scopes of members with the same name and type to exist in both a nested class and the enclosing class simultaneously. Depending on which object we use to call the same variable in a main method will result in different outputs.

class Book {
  String type="Nonfiction";
	// Nested inner class
	class Biography {
    String type="Biography";

    public void print(){
      System.out.println(Book.this.type);
      System.out.println(type);
    }

	}
}

public class Books {
	public static void main(String[] args) {
		Book book = new Book();
		Book.Biography bio = book.new Biography();
		bio.print();
	}
}
//Nonfiction
//Biography
class Lib { 
  String objType;
  String objName;
  static String libLocation = "Midfield St.";

  public Lib(String type, String name) {
    this.objType = type;
    this.objName = name;
  }

  private String getObjName() {
    return this.objName;
  }

  // inner class
  static class Book {
    String description;

    void printLibraryLocation(){
      System.out.println("Library location: "+libLocation);
    }
  }
}

public class Main {
  public static void main(String[] args) {
    Lib.Book book =  new Lib.Book();
    book.printLibraryLocation();

  }
}
class Lib {
  String objType;
  String objName;

  // Assign values using constructor
  public Lib(String type, String name) {
    this.objType = type;
    this.objName = name;
  }

  // private method
  private String getObjName() {
    return this.objName;
  }

  // Inner class
  class Book {
    String description;

    void setDescription() {
      if(Lib.this.objType.equals("book")) {
        if(Lib.this.getObjName().equals("nonfiction")) {
          this.description = "Factual stories/accounts based on true events";
        } else {
          this.description = "Literature that is imaginary.";
        }
      } else {
        this.description = "Not a book!";
        }
    }
    String getDescription() {
      return this.description;
    }
  }
}

public class Main {
  public static void main(String[] args) {
    Lib fiction = new Lib("book", "fiction");

    Lib.Book book1 = fiction.new Book();
    book1.setDescription();
    System.out.println("Fiction Book Description = " + book1.getDescription());
 
    Lib nonFiction = new Lib("book", "nonfiction");
    Lib.Book book2 = nonFiction.new Book();
    book2.setDescription();
    System.out.println("Non-fiction Book Description = " + book2.getDescription());
  }
}
class Outer {
  String outer;
  // Assign values using constructor
  public Outer(String name) {
    this.outer = name;
  }
 
  // private method
  private String getName() {
    return this.outer;
  }
}
 
  // Non-static nested class
  class Inner {
    String inner;
    String outer;
    
    public String getOuter() {
  // Instantiate outer class to use its method
  outer = Outer.this.getName();
}
// Given `intList` with the following elements: 5, 4, 1, 3, 7, 8
List<Integer> evenList = new ArrayList<>();
for(Integer i: intList) {
  if(i % 2 == 0) {
    evenList.add(i*2);
  }
}
// evenList will have elements 8, 16
/*A Stream is a sequence of elements created from a Collection source. A Stream can be used as input to a pipeline, which defines a set of aggregate operations (methods that apply transformations to a Stream of data). The output of an aggregate operation serves as the input of the next operation (these are known as intermediate operations) until we reach a terminal operation, which is the final operation in a pipeline that produces some non Stream output.*/
List<Integer> evenList = intList.stream()
  .filter((number) -> {return number % 2 == 0;})
  .map( evenNum -> evenNum*2)
  .collect(Collectors.toList());

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import java.util.Random;
public class Main {
  public static void main(String[] args) {
    List<Integer> myInts = new ArrayList<>();
    Random random = new Random();

    for(int i =0; i < 20; i++) {
      myInts.add(random.nextInt(5));
    }

    Map<Integer, Integer> intCount = countNumbers(myInts);
    for(Map.Entry<Integer, Integer> entry: intCount.entrySet()) {
      System.out.println("Integer: "+ entry.getKey()+" appears: "+ entry.getValue());
    }
  }

  public static Map<Integer, Integer> countNumbers(List<Integer> list) {
    Map<Integer, Integer> intCount = new TreeMap<>();
    for(Integer i: list){
      Integer currentCount = intCount.get(i);
      if(currentCount!=null){
        int newCount = currentCount+1;
        intCount.put(i, newCount);}
      else{intCount.put(i, 1);}
    }
    return intCount;
  }

}

/*
Output:
Integer: 0 appears: 3
Integer: 1 appears: 2
Integer: 2 appears: 8
Integer: 3 appears: 1
Integer: 4 appears: 6
//Map defines a generic interface for an object that holds key-value pairs as elements. The key is used to retrieve (like the index in an array or List) some value. A key must be unique and map to exactly one value.
//The HashMap defines no specific ordering for the keys and is the most optimized implementation for retrieving values.
//The LinkedHashMap keeps the keys in insertion order and is slightly less optimal than the HashMap.
//The TreeMap keeps keys in their natural order (or some custom order defined using a Comparator). This implementation has a significant performance decrease compared to HashMap and LinkedHashMap but is great when needing to keep keys sorted.
//A Map has the following methods for accessing its elements:
//put(): Sets the value that key maps to. Note that this method replaces the value key mapped to if the key was already present in the Map.
//get(): Gets, but does not remove, the value the provided key argument points to if it exists. This method returns null if the key is not in the Map.

Map<String, String> myMap = new HashMap<>();
 
myMap.put("Hello", "World") // { "Hello" -> "World" }
myMap.put("Name", "John") //   { "Hello" -> "World" }, { "Name" -> "John" }
 
String result = myMap.get("Hello") // returns "World" 
String noResult = myMap.get("Jack") // return `null`

// Given a map, `myMap`, with the following key-value pairs { "Hello" -> "World" }, { "Name" -> "John"}
for (Map.Entry<String, String> pair: myMap.entrySet()){
  System.out.println("key: "+pair.getKey()+", value: "+pair.getValue());
}
// OUTPUT TERMINAL:
// key: Name, value: John
// key: Hello, value: World
//There are many methods provided in the Collections class and we’ll cover a subset of those below:
//binarySearch(): Performs binary search over a List to find the specified object and returns the index if found. This method is overloaded to also accept a Comparator in order to define a custom sorting algorithm.
//max(): Finds and returns the maximum element in the Collection. This method is overloaded to also accept a Comparator in order to define a custom sorting algorithm.
//min(): Finds and returns the minimum element in the Collection. This method is overloaded to also accept a Comparator in order to define a custom sorting algorithm.
//reverse(): Reverses the order of elements in the List passed in as an argument.
//sort(): Sorts the List passed in as an argument. This method is overloaded to also accept a Comparator in order to define a custom sorting algorithm.
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collection;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
public class Main {
  public static void main(String[] args) {
    List<Integer> myList = new ArrayList<>();
    myList.add(3);
    myList.add(-1);
    myList.add(57);
    myList.add(29);

    Set<String> mySet = new HashSet<>();
    mySet.add("Hello");
    mySet.add("World");

    System.out.println("mySet max: \""+ Collections.max(mySet)+"\"");
    System.out.println();

    System.out.println("myList min: "+ Collections.min(myList));
    System.out.println();

    System.out.println("Index of 57 in myList is: "+Collections.binarySearch(myList, 57));
    System.out.println();


    System.out.println("myList prior to reverse: ");
    printCollection(myList);

    System.out.println();

    Collections.reverse(myList);
    System.out.println("myList reversed: ");
    printCollection(myList);

    System.out.println();

    System.out.println("myList prior to sort: ");
    printCollection(myList);

    System.out.println();

    Collections.sort(myList);
    System.out.println("myList after sort: ");
    printCollection(myList);


  }

  public static void printCollection(Collection<?> collection){
    Iterator<?> myItr = collection.iterator();

    while(myItr.hasNext()){
      System.out.println(myItr.next());
    }
  }
}
//the Collection interface provides a generic, general-purpose API when our program needs a collection of elements and doesn’t care about what type of collection it is.
//Implementing classes may implement collections methods and add restrictions to them, like a Set does to only contain unique elements. Also, implementing classes or extending interfaces do not need to implement all methods and instead will throw an UnsupportOperationException when a Collection method is not implemented.
//We’ve seen add() and remove() be used but some other methods Collection defines are:
//addAll() - receives a Collection argument and adds all the elements.
//isEmpty() - return true if the collection is empty, false otherwise.
//iterator() - returns an Iterator over the collection.
//size() - returns the number of elements in the collection.
//stream() - returns a Stream over the elements in the collection.
//toArray() - returns an array with all elements in the collection.

Collection<Integer> collection = new ArrayList<>();
collection.add(4);
collection.add(8);
 
boolean isEmpty = collection.isEmpty(); // false
int collectionSize = collection.size(); // 2
 
Integer[] collectionArray = collection.toArray(new Integer[0]);

private static <T> void printCollection(Collection<T> collection) {
    for(T item: collection){
      System.out.println(item);}
  }
//A Deque is a collection that allows us to manipulate elements from both the front and end of the collection.
//The Deque interface has two types of methods for manipulating the front and back of the collection.
//The following methods throw an exception when:
//addFirst(), addLast() - there is no space to add an element.
//removeFirst(), removeLast() - there is no element to remove.
//getFirst(), getLast() - there is no element to get.
//The following methods return a special value:
//offerFirst(), offerLast() - false when there is no space to add an element.
//pollFirst(), pollLast() - null when there is no element to remove.
//peekFirst(), peekLast() - null when there is no element to get.

Deque<String> stringDeque = new LinkedList<>();
stringDeque.addFirst("A"); // Front -> "A" <- end
stringDeque.offerFirst("B"); // Return `true` - front -> "B", "A" <- end
stringDeque.offerLast("Z"); // Returns `true` - front -> "B", "A", "Z" <- end
 
String a = stringDeque.removeFirst()  // Returns "B" - front -> "A", "Z"
String b = stringDeque.pollLast()  // Returns "Z" - front -> "A" <- back
String c = stringDeque.removeLast()  // Returns "A" - empty deque
 
String d = stringDeque.peekFirst()  // Returns null
String e = stringDeque.getLast() // Throws NoSuchElementException

// Assuming `stringDeque` has elements front -> "Mike", "Jack", "John" <- back
Iterator<String> descItr = stringDeque.descendingIterator();
 
while(descItr.hasNext()) {
  System.out.println(descItr.next());
}
// OUTPUT TERMINAL:  "John", "Jack", "Mike"
//collection that stores elements that can be accessed at some later point to process (like waiting in line at the bank teller). A Queue accesses elements in a (usually) First In First Out (FIFO) manner where elements are inserted at the tail (back) of the collection and removed from the head (front).
//A Queue has two types of access methods for inserting, removing, and getting but not removing the head of the Queue.
//The following methods throw an exception when:
//add() - there is no space for the element
//remove() - there are no elements to remove
//element() - there are no elements to get
//The following methods return a special value:
//offer() - false there is no space for the element
//poll() - null there are no elements to remove
//peek() - null there are no elements to get
//The methods that return a special value should be used when working with a statically sized Queue and the exception throwing methods when using a dynamic Queue.

Queue<String> stringQueue = new LinkedList<>();
stringQueue.add("Mike"); // true - state of queue -> "Mike"
stringQueue.offer("Jeff"); // true - state of queue -> "Mike", "Jeff" 
 
String a = stringQueue.remove() // Returns "Mike" - state of queue -> 1
String b = stringQueue.poll() // Returns "Jeff" - state of queue -> empty
String c = stringQueue.peek() // Returns null
String d = stringQueue.element() // Throws NoSuchElementException

// Assuming `stringQueue` has elements -> "Mike", "Jack", "John"
for (String name: stringQueue) {
  System.out.println(name);
}
// OUTPUT TERMINAL: "Mike", "Jack", "John"
//A Set is a collection of unique elements and all of its methods ensure this stays true. 
//The HashSet implementation has the best performance when retrieving or inserting elements but cannot guarantee any ordering among them.
//The TreeSet implementation does not perform as well on insertion and deletion of elements but does keep the elements stored in order based on their values (this can be customized).
//The LinkedHashSet implementation has a slightly slower performance on insertion and deletion of elements than a HashSet but keeps elements in insertion order.

Set<Integer> intSet = new HashSet<>();  // Empty set
intSet.add(6);  // true - 6  
intSet.add(0);  //  true - 0, 6 (no guaranteed ordering)
intSet.add(6);  //  false - 0, 6 (no change, no guaranteed ordering)
 
boolean isNineInSet = intSet.contains(9);  // false
boolean isZeroInSet = intSet.contains(0);  // true

// Assuming `intSet` has elements -> 1, 5, 9, 0, 23
for (Integer number: intSet) {
  System.out.println(number);
}
// OUTPUT TERMINAL: 5 0 23 9 1
//. A List is a collection where its elements are ordered in a sequence. Lists allow us to have duplicate elements and fine-grain control over where elements are inserted in the sequence. Lists are dynamically sized.
import java.util.List;
import java.util.ArrayList;
public class Main {
  public static void main(String[] args) {
    List<String> stringList = new ArrayList<>();
    stringList.add("Hello");
    stringList.add("World");
    stringList.add("!");
    for(String element: stringList){
      System.out.println(element);}
  }
}
public class Util {
  public static void printBag(Bag<?> bag ) {
    System.out.println(bag.toString()); 
  }
}
Bag<String> myBag1 = new Bag("Hello");
Bag<Integer> myBag2 = new Bag(23);
Util.printBag(myBag1);  // Hello
Util.printBag(myBag2);  // 23

public static <T> void printBag(Bag<T> bag ) {
  System.out.println(bag.toString()); 
}

public static <T> Bag<T> getBag(Bag<T> bag ) {
  return bag;
}

public static void printBag(Bag<? extends Number> bag ) {
  System.out.println(bag.toString()); 
}

//Wildcard Lower Bounds
//A lower bound wildcard restricts the wildcard to a class or interface and any of its parent types.
//There are some general guidelines provided by Java as to when to use what type of wildcard:

//An upper bound wildcard should be used when the variable is being used to serve some type of data to our code.
//A lower bound wildcard should be used when the variable is receiving data and holding it to be used later.
//When a variable that serves data is used and only uses Object methods, an unbounded wildcard is preferred.
//When a variable needs to serve data and store data for use later on, a wildcard should not be used (use a type parameter instead).
//An upper bound restricts the type parameter to a class or any of its sub-classes and is done this way: SomeClass<? extends SomeType>. A lower bound restricts the type parameter to a class or any of its parent classes and is done this way: SomeClass<? super SomeType>.
public class Util {
  public static void getBag(Bag<? super Integer> bag ) {
    return bag;
  }
}
public class Box <T extends Number> {
  private T data; 
}
 
Box<Integer> intBox = new Box<>(2);  // Valid type argument
Box<Double> doubleBox = new Box<>(2.5);  // Valid type argument
Box<String> stringBox = new Box<>("hello");  // Error

public static <T extends Number> boolean isZero(T data) {
  return data.equals(0);
}

//multiple bounds
public class Box <T extends Number & Comparable<T>> {
  private T data; 
}
public class Box<T, S> {
  private T item1;
  private S item2;
  // Constructors, getters, and setters
}
Box<String, Integer> wordAndIntegerBox = new Box<>("Hello", 5);

public class Util {
  public static <T, S> boolean areNumbers(T item1, S item2) {
    return item1 instanceof Number && item2 instanceof Number; 
  }
}
 
boolean areNums = Util.areNumbers("Hello", 34);  // false
star

Thu Dec 29 2022 18:57:56 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Thu Dec 29 2022 18:56:17 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Thu Dec 29 2022 18:43:41 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Thu Dec 29 2022 18:40:20 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Thu Dec 29 2022 12:48:08 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Thu Dec 29 2022 12:42:39 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Mon Dec 26 2022 14:29:07 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Mon Dec 26 2022 13:52:57 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Sun Dec 25 2022 22:20:57 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Sun Dec 25 2022 22:17:51 GMT+0000 (Coordinated Universal Time)

#java #generics #map
star

Sun Dec 25 2022 22:04:31 GMT+0000 (Coordinated Universal Time)

#java #generics #map
star

Sun Dec 25 2022 21:59:38 GMT+0000 (Coordinated Universal Time)

#java #generics #algorithms
star

Sun Dec 25 2022 21:56:15 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Sun Dec 25 2022 21:13:03 GMT+0000 (Coordinated Universal Time)

#java #generics #deque
star

Sun Dec 25 2022 20:59:46 GMT+0000 (Coordinated Universal Time)

#java #generics #queue
star

Sun Dec 25 2022 20:54:50 GMT+0000 (Coordinated Universal Time)

#java #generics #set
star

Sun Dec 25 2022 20:47:50 GMT+0000 (Coordinated Universal Time)

#java #generics #list
star

Sun Dec 25 2022 20:25:16 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Sun Dec 25 2022 20:21:43 GMT+0000 (Coordinated Universal Time)

#java #generics
star

Sun Dec 25 2022 20:15:13 GMT+0000 (Coordinated Universal Time)

#java #generics

Save snippets that work with our extensions

Available in the Chrome Web Store Get Firefox Add-on Get VS Code extension