CS 330 Lecture 39 – Metaprogramming

Agenda

  • what ?s
  • JSON to Object
  • memoize
  • @Override
  • writing a test runner

Intentions

  • I am familiar with the practice of metaprogramming, in which a program can inspect and act on itself—i.e., the program becomes another piece of data than can be inspected and manipulated.
  • I am aware of several possible benefits of metaprogramming, including¬†dynamic¬†generation of new program features and objects and tools that automatically¬†profile, test, or transform a program to provide services beyond the traditional compiler.

Code

autobject.rb

#!/usr/bin/env ruby

require 'json'
require 'open-uri'

class JObject
  def initialize data
    @data = data
  end

  def self.load io
    self.new(JSON.load(io))
  end

  def method_missing name
    name = name.to_s
    if @data.has_key? name
      value = @data[name]
      if value.is_a? Hash
        JObject.new(@data[name])
      else
        value
      end
    else
      super
    end
  end
end

jo = JObject.load(open 'http://api.openweathermap.org/data/2.5/weather?q=Eau%20Claire,WI')

puts Time.at(jo.sys.sunset)

Superclass.java

package reflecting;

public class Superclass {
  public int get() {
    return 5;
  }
}

Subclass.java

package reflecting;

public class Subclass extends Superclass {
  @Usurps
  public int getX() {
    return 6;
  }
  
  public int getZ() {
    return 0;
  }
}

Usurps.java

package reflecting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Usurps {
}

TestUsurps.java

package reflecting;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class TestUsurps {
  public static void main(String[] args) {
    test(Subclass.class);
  }
  
  public static void test(Class<?> clazz) {
    for (Method m : clazz.getDeclaredMethods()) {
      Annotation usurps = m.getAnnotation(Usurps.class);
      if (usurps != null) {
        Class<?> superclazz = clazz.getSuperclass();
        try {
          superclazz.getMethod(m.getName(), m.getParameterTypes());
        } catch (NoSuchMethodException e) {
          System.err.println(m.getName() + " usurps nothing in " + superclazz.getName());
        }
      }
    }
  }
}

Vector2D.java

package reflecting;

public class Vector2D {
  private double x;
  private double y;
  
  public Vector2D() {
  }
  
  public Vector2D(double x,
                  double y) {
    this.x = x;
    this.y = y;
  }
  
  public double getX() {
    return x;
  }
  
  public double getY() {
    return y;
  }
  
  public double getLength() {
    return x * x + y * y;
  }
  
  public void normalize() {
    x /= getLength();
    y /= getLength();
  }
}

TestTest.java

package reflecting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface TestTest {
}

TestVector2D.java

package reflecting;

public class TestVector2D {
  private static final double EPSILON = 1.0e-3;
  
  @TestTest
  public static void testDefaultCtor() {
    Vector2D v = new Vector2D();
    Assert.assertAlmostEquals("Default ctor zeroes x", 0, v.getX(), EPSILON);
    Assert.assertAlmostEquals("Default ctor zeroes y", 0, v.getY(), EPSILON);
  }
  
  @TestTest
  public static void testOtherCtor() {
    Vector2D v = new Vector2D(6, 7);
    Assert.assertAlmostEquals("Default ctor sets x", 6, v.getX(), EPSILON);
    Assert.assertAlmostEquals("Default ctor sets y", 7, v.getY(), EPSILON);
  }
}

TestRunner.java

package reflecting;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestRunner {
  public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    test(TestVector2D.class);
  }

  public static void test(Class<?> clazz) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    for (Method m : clazz.getDeclaredMethods()) {
      Annotation testtest = m.getAnnotation(TestTest.class);
      if (testtest != null) {
        try {
          m.invoke(null);
        } catch (InvocationTargetException e) {
          if (e.getCause().getClass() == Assert.AssertException.class) {
            System.err.println(e.getCause().getMessage());
          } else {
            throw e;
          }
        }
      }
    }
  }
}

Haiku

on a blind date
“So, how was your day?”
My date—a vampire—blinked twice
“We don’t reflect much”

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *